Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

276 righe
9.4 KiB

  1. #pragma once
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include "types.hpp"
  6. #include "arraylist.hpp"
  7. u32 hm_hash(const char* str) {
  8. u32 value = str[0] << 7;
  9. s32 i = 0;
  10. while (str[i]) {
  11. value = (10000003 * value) ^ str[i++];
  12. }
  13. return value ^ i;
  14. }
  15. u32 hm_hash(char* str) {
  16. u32 value = str[0] << 7;
  17. s32 i = 0;
  18. while (str[i]) {
  19. value = (10000003 * value) ^ str[i++];
  20. }
  21. return value ^ i;
  22. }
  23. u32 hm_hash(void* ptr) {
  24. return ((u64)ptr * 2654435761) % 4294967296;
  25. }
  26. inline bool hm_objects_match(const char* a, const char* b) {
  27. return strcmp(a, b) == 0;
  28. }
  29. inline bool hm_objects_match(char* a, char* b) {
  30. return strcmp(a, b) == 0;
  31. }
  32. inline bool hm_objects_match(void* a, void* b) {
  33. return a == b;
  34. }
  35. template <typename key_type, typename value_type>
  36. struct Hash_Map {
  37. u32 current_capacity;
  38. u32 cell_count;
  39. struct HM_Cell {
  40. key_type original;
  41. u64 hash;
  42. enum struct Occupancy : u8 {
  43. Avaliable = 0,
  44. Occupied,
  45. Deleted
  46. } occupancy;
  47. value_type object;
  48. }* data;
  49. template <typename lambda>
  50. void for_each(lambda p) {
  51. for(u32 index = 0; index < current_capacity; ++index)
  52. if (data[index].occupancy == HM_Cell::Occupancy::Occupied)
  53. p(data[index].original, data[index].object, index);
  54. }
  55. void alloc(u32 initial_capacity = 8) {
  56. // round up to next pow of 2
  57. --initial_capacity;
  58. initial_capacity |= initial_capacity >> 1;
  59. initial_capacity |= initial_capacity >> 2;
  60. initial_capacity |= initial_capacity >> 4;
  61. initial_capacity |= initial_capacity >> 8;
  62. initial_capacity |= initial_capacity >> 16;
  63. ++initial_capacity;
  64. // until here
  65. current_capacity = initial_capacity;
  66. cell_count = 0;
  67. data = (HM_Cell*)calloc(initial_capacity, sizeof(HM_Cell));
  68. }
  69. void dealloc() {
  70. free(data);
  71. data = nullptr;
  72. }
  73. s32 get_index_of_living_cell_if_it_exists(key_type key, u64 hash_val) {
  74. ZoneScoped;
  75. s32 index = hash_val & (current_capacity - 1);
  76. HM_Cell cell = data[index];
  77. /* test if there is or was something there */
  78. if (cell.occupancy != HM_Cell::Occupancy::Avaliable) {
  79. /* check if objects match */
  80. if (hm_objects_match(key, cell.original)) {
  81. ZoneScopedN("hm_objects_match check");
  82. /* we found it, now check it it is deleted: */
  83. if (cell.occupancy == HM_Cell::Occupancy::Deleted) {
  84. /* we found it but it was deleted, we */
  85. /* dont have to check for collisions then */
  86. return -1;
  87. } else {
  88. /* we found it and it is not deleted */
  89. return index;
  90. }
  91. } else {
  92. /* objects dont match, this means we have */
  93. /* a collision. We just search forward */
  94. for (u32 i = 0; i < current_capacity; ++i) {
  95. u32 new_idx = (i + index) & (current_capacity - 1);
  96. cell = data[new_idx];
  97. /* If we find a avaliable cell while looking */
  98. /* forward, the object is not in the hm */
  99. if (cell.occupancy == HM_Cell::Occupancy::Avaliable)
  100. return -1;
  101. /* If the objects don't match, keep looking */
  102. if (!hm_objects_match(key, cell.original))
  103. continue;
  104. /* TODO(Felix): If the objects do match, */
  105. /* and it is deleted, we should return -1? */
  106. if (cell.occupancy == HM_Cell::Occupancy::Deleted)
  107. continue;
  108. return new_idx;
  109. }
  110. /* not or only deleted cells found */
  111. return -1;
  112. }
  113. } else {
  114. /* no cell exists at this index so the item was never in the */
  115. /* hashmap. Either it would be there or be ther and 'deleted' */
  116. /* or another item would be there and therefore a collistion */
  117. /* would exist */
  118. return -1;
  119. }
  120. }
  121. bool key_exists(key_type key) {
  122. ZoneScoped;
  123. return get_index_of_living_cell_if_it_exists(key, hm_hash((key_type)key)) != -1;
  124. }
  125. key_type search_key_to_object(value_type v) {
  126. for (u32 i = 0; i < current_capacity; ++i) {
  127. if (data[i].object == v &&
  128. data[i].occupancy == HM_Cell::Occupancy::Occupied)
  129. {
  130. return data[i].original;
  131. }
  132. }
  133. return nullptr;
  134. }
  135. Array_List<key_type> get_all_keys() {
  136. Array_List<key_type> ret;
  137. ret.alloc();
  138. // QUESTION(Felix): Does it make sense to
  139. // ret.reserve(this->cell_count)?
  140. for (u32 i = 0; i < current_capacity; ++i) {
  141. if (data[i].occupancy == HM_Cell::Occupancy::Occupied)
  142. ret.append(data[i].original);
  143. }
  144. return ret;
  145. }
  146. value_type get_object(key_type key, u64 hash_val) {
  147. s32 index = get_index_of_living_cell_if_it_exists(key, hash_val);
  148. if (index != -1) {
  149. return data[index].object;
  150. }
  151. return 0;
  152. }
  153. value_type get_object(key_type key) {
  154. ZoneScoped;
  155. return get_object(key, hm_hash((key_type)key));
  156. }
  157. value_type* get_object_ptr(key_type key, u64 hash_val) {
  158. s32 index = get_index_of_living_cell_if_it_exists(key, hash_val);
  159. if (index != -1) {
  160. return &(data[index].object);
  161. }
  162. return 0;
  163. }
  164. value_type* get_object_ptr(key_type key) {
  165. return get_object_ptr(key, hm_hash((key_type)key));
  166. }
  167. void delete_object(key_type key) {
  168. s32 index = get_index_of_living_cell_if_it_exists(key, hm_hash((key_type)key));
  169. if (index != -1) {
  170. data[index].occupancy = HM_Cell::Occupancy::Deleted;
  171. }
  172. }
  173. void set_object(key_type key, value_type obj, u64 hash_val) {
  174. ZoneScoped;
  175. u32 index = hash_val & (current_capacity - 1);
  176. /* if we the desired cell is avaliable, write to it and done :) */
  177. if (data[index].occupancy == HM_Cell::Occupancy::Avaliable) {
  178. /* insert new cell into desired slot */
  179. ++cell_count;
  180. } else {
  181. if (hm_objects_match(key, data[index].original)) {
  182. /* overwrite object with same key, dont increment cell */
  183. /* count */
  184. } else {
  185. /* collision, check resize */
  186. if ((cell_count*1.0f / current_capacity) > 0.666f) {
  187. ZoneScopedN("HM Resize");
  188. auto old_data = data;
  189. data = (HM_Cell*)calloc(current_capacity*4, sizeof(HM_Cell));
  190. cell_count = 0;
  191. current_capacity *= 4;
  192. /* insert all old items again */
  193. for (u32 i = 0; i < current_capacity/4; ++i) {
  194. auto cell = old_data[i];
  195. if (cell.occupancy == HM_Cell::Occupancy::Occupied) {
  196. set_object(cell.original, cell.object, cell.hash);
  197. }
  198. }
  199. free(old_data);
  200. index = hash_val & (current_capacity - 1);
  201. }
  202. ++cell_count;
  203. /* search for empty slot for new cell starting at desired index; */
  204. /* preventing gotos using lambdas! */
  205. [&]{
  206. for (u32 i = index; i < current_capacity; ++i) {
  207. if (data[i].occupancy == HM_Cell::Occupancy::Avaliable ||
  208. hm_objects_match(data[i].original, key))
  209. {
  210. index = i;
  211. return;
  212. }
  213. }
  214. for (u32 i = 0; i < index; ++i) {
  215. if (data[i].occupancy == HM_Cell::Occupancy::Avaliable ||
  216. hm_objects_match(data[i].original, key))
  217. {
  218. index = i;
  219. return;
  220. }
  221. }
  222. }();
  223. }
  224. }
  225. data[index].occupancy = HM_Cell::Occupancy::Occupied;
  226. data[index].original = key;
  227. data[index].hash = hash_val;
  228. data[index].object = obj;
  229. }
  230. void set_object(key_type key, value_type obj) {
  231. u64 hash_val = hm_hash((key_type)key);
  232. set_object(key, obj, hash_val);
  233. }
  234. void dump_occupancy(const char* path) {
  235. FILE* out = fopen(path, "w");
  236. defer { fclose(out); };
  237. for (u32 i = 0; i < current_capacity; ++i) {
  238. if (data[i].occupancy == HM_Cell::Occupancy::Avaliable) {
  239. fprintf(out, "%04u [FREE]\n", i);
  240. } else if (data[i].occupancy == HM_Cell::Occupancy::Deleted) {
  241. fprintf(out, "%04u [DELETED] hash: %llu (wants to be %llu)\n", i, data[i].hash, data[i].hash & (current_capacity - 1));
  242. } else {
  243. fprintf(out, "%04u [OCCUPIED] hash: %llu (wants to be %llu)\n", i, data[i].hash, data[i].hash & (current_capacity - 1));
  244. }
  245. }
  246. }
  247. };