Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

170 строки
12 KiB

  1. #pragma once
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include "types.hpp"
  6. // inline void print_hm(StringHashMap* hm) {
  7. // for_str_hash_map(hm) {
  8. // printf("index: %d desired-index: %llu key: %s hash: %llu value: %llu\n", i, (hm->data[i].hash % hm->current_capacity), (char*)key, hm->data[i].hash, (unsigned long long)value);
  9. // }
  10. // }
  11. #define __for_hm_generator(key_type, hm) \
  12. if (key_type key = nullptr); else \
  13. if (void* value = nullptr); else \
  14. for(int index = 0; index < hm->current_capacity; ++index) \
  15. if (!((!hm->data[index].deleted) && \
  16. (key = hm->data[index].original) && \
  17. (value = hm->data[index].object))); else
  18. #define define_hash_map(type, name) \
  19. bool hm_objects_match(type,type); \
  20. u32 hm_hash(type); \
  21. \
  22. struct name##_Hash_Map_Cell { \
  23. type original; \
  24. u64 hash; \
  25. bool deleted; \
  26. void* object; \
  27. }; \
  28. \
  29. struct name##_Hash_Map { \
  30. int current_capacity; \
  31. int cell_count; \
  32. name##_Hash_Map_Cell* data; \
  33. }; \
  34. \
  35. name##_Hash_Map* create_##name##_hashmap(int initial_capacity=8) { \
  36. name##_Hash_Map* hm = new name##_Hash_Map; \
  37. hm->current_capacity = initial_capacity; \
  38. hm->cell_count = 0; \
  39. /* set all data to nullptr */ \
  40. hm->data = (name##_Hash_Map_Cell*)calloc(initial_capacity, sizeof(name##_Hash_Map_Cell)); \
  41. /* printf("check: %s\n", hm->data[6].original_string); */ \
  42. return hm; \
  43. } \
  44. \
  45. int hm_get_index_of_living_cell_if_it_exists(name##_Hash_Map* hm, type key, u64 hash_val) { \
  46. int index = hash_val % hm->current_capacity; \
  47. name##_Hash_Map_Cell cell = hm->data[index]; \
  48. /* test if cell exists at that index */ \
  49. if (cell.original) { \
  50. /* check if strings match */ \
  51. if (hm_objects_match(key, cell.original)) { \
  52. /* we found it, now check it it is deleted: */ \
  53. if (cell.deleted) { \
  54. /* we found it but it was deleted, we */ \
  55. /* dont have to check for collisions then */ \
  56. return -1; \
  57. } else { \
  58. /* we found it and it is not deleted */ \
  59. return index; \
  60. } \
  61. } else { \
  62. /* strings dont match, this means we have */ \
  63. /* a collision. We just search forward */ \
  64. for (int i = 0; i < hm->current_capacity; ++i) { \
  65. int new_idx = (i + index) % hm->current_capacity; \
  66. cell = hm->data[new_idx]; \
  67. if (!cell.original) \
  68. return -1; \
  69. if (!hm_objects_match(key, cell.original)) \
  70. continue; \
  71. if (cell.deleted) \
  72. continue; \
  73. return new_idx; \
  74. } \
  75. /* not or only deleted cells found */ \
  76. return -1; \
  77. } \
  78. } else { \
  79. /* no cell exists at this index so the item was never in the */ \
  80. /* hashmap. Either it would be there or be ther and 'deleted' */ \
  81. /* or another item would be there and therefore a collistion */ \
  82. /* would exist */ \
  83. return -1; \
  84. } \
  85. } \
  86. \
  87. inline bool hm_key_exists(name##_Hash_Map* hm, type key) { \
  88. return hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)) != -1; \
  89. } \
  90. \
  91. inline void* hm_get_object(name##_Hash_Map* hm, type key) { \
  92. int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)); \
  93. if (index != -1) { \
  94. return hm->data[index].object; \
  95. } \
  96. return nullptr; \
  97. } \
  98. \
  99. inline void hm_delete_object(name##_Hash_Map* hm, type key) { \
  100. int index = hm_get_index_of_living_cell_if_it_exists(hm, key, hm_hash(key)); \
  101. if (index != -1) { \
  102. hm->data[index].deleted = true; \
  103. } \
  104. } \
  105. \
  106. void hm_set(name##_Hash_Map* hm, type key, void* obj) { \
  107. u64 hash_val = hm_hash(key); \
  108. int index = hash_val % hm->current_capacity; \
  109. \
  110. /* if we the desired cell is just empty, write to it and done :) */ \
  111. if (!hm->data[index].original) { \
  112. /* insert new cell into desired slot */ \
  113. ++hm->cell_count; \
  114. } else { \
  115. if (hm_objects_match(key, hm->data[index].original)) { \
  116. /* overwrite object with same key, dont increment cell */ \
  117. /* count */ \
  118. } else { \
  119. /* collision, check resize */ \
  120. ++hm->cell_count; \
  121. if ((hm->cell_count*1.0f / hm->current_capacity) > 0.666f) { \
  122. auto old_data = hm->data; \
  123. hm->data = (name##_Hash_Map_Cell*)calloc(hm->current_capacity*4, sizeof(name##_Hash_Map_Cell)); \
  124. hm->cell_count = 0; \
  125. hm->current_capacity *= 4; \
  126. \
  127. /* insert all old items again */ \
  128. for (int i = 0; i < hm->current_capacity/4; ++i) { \
  129. auto cell = old_data[i]; \
  130. if (cell.original) { \
  131. hm_set(hm, cell.original, cell.object); \
  132. } \
  133. } \
  134. free(old_data); \
  135. index = hash_val % hm->current_capacity; \
  136. } \
  137. /* search for empty slot for new cell starting at desired index; */ \
  138. /* preventing gotos using lambdas! */ \
  139. [&](){ \
  140. for (int i = index; i < hm->current_capacity; ++i) { \
  141. if (!hm->data[i].original || \
  142. hm_objects_match(hm->data[i].original, key)) \
  143. { \
  144. index = i; \
  145. return; \
  146. } \
  147. } \
  148. for (int i = 0; i < index; ++i) { \
  149. if (!hm->data[i].original || \
  150. hm_objects_match(hm->data[i].original, key)) \
  151. { \
  152. index = i; \
  153. return; \
  154. } \
  155. } \
  156. }(); \
  157. } \
  158. } \
  159. \
  160. hm->data[index].deleted = false; \
  161. hm->data[index].original = key; \
  162. hm->data[index].hash = hash_val; \
  163. hm->data[index].object = obj; \
  164. } \