map_key_impl.c.h (4572B)
1 #include "nvim/map_defs.h" 2 #include "nvim/memory.h" 3 4 #ifndef KEY_NAME 5 // Don't error out. it is nice to type-check the file in isolation, in clangd or otherwise 6 # define KEY_NAME(x) x##int 7 # define hash_int(x) ((uint32_t)x) 8 # define equal_int(x, y) ((x) == (y)) 9 #endif 10 11 #define SET_TYPE KEY_NAME(Set_) 12 #define KEY_TYPE KEY_NAME() 13 14 /// find bucket to get or put "key" 15 /// 16 /// set->h.hash assumed already allocated! 17 /// 18 /// @return bucket index, or MH_TOMBSTONE if not found and `put` was false 19 /// mh_is_either(hash[rv]) : not found, but this is the place to put 20 /// otherwise: hash[rv]-1 is index into key/value arrays 21 uint32_t KEY_NAME(mh_find_bucket_)(SET_TYPE *set, KEY_TYPE key, bool put) 22 { 23 MapHash *h = &set->h; 24 uint32_t step = 0; 25 uint32_t mask = h->n_buckets - 1; 26 uint32_t k = KEY_NAME(hash_)(key); 27 uint32_t i = k & mask; 28 uint32_t last = i; 29 uint32_t site = put ? last : MH_TOMBSTONE; 30 while (!mh_is_empty(h, i)) { 31 if (mh_is_del(h, i)) { 32 if (site == last) { 33 site = i; 34 } 35 } else if (KEY_NAME(equal_)(set->keys[h->hash[i] - 1], key)) { 36 return i; 37 } 38 i = (i + (++step)) & mask; 39 if (i == last) { 40 abort(); 41 } 42 } 43 if (site == last) { 44 site = i; 45 } 46 return site; 47 } 48 49 /// @return index into set->keys if found, MH_TOMBSTONE otherwise 50 uint32_t KEY_NAME(mh_get_)(SET_TYPE *set, KEY_TYPE key) 51 { 52 if (set->h.n_buckets == 0) { 53 return MH_TOMBSTONE; 54 } 55 uint32_t idx = KEY_NAME(mh_find_bucket_)(set, key, false); 56 return (idx != MH_TOMBSTONE) ? set->h.hash[idx] - 1 : MH_TOMBSTONE; 57 } 58 59 /// Rebuild hash from keys[] array 60 /// 61 /// set->h.hash must be allocated and empty before&alling! 62 void KEY_NAME(mh_rehash_)(SET_TYPE *set) 63 { 64 for (uint32_t k = 0; k < set->h.n_keys; k++) { 65 uint32_t idx = KEY_NAME(mh_find_bucket_)(set, set->keys[k], true); 66 // there must be tombstones when we do a rehash 67 if (!mh_is_empty((&set->h), idx)) { 68 abort(); 69 } 70 set->h.hash[idx] = k + 1; 71 } 72 set->h.n_occupied = set->h.size = set->h.n_keys; 73 } 74 75 /// Put a key. Return the existing item if found 76 /// 77 /// Allocates/resizes the hash table and/or keys[] table if needed. 78 /// 79 /// @param[out] new mandatory. Reveals if an existing key was found. In addition, 80 /// if new item, indicates if keys[] was resized. 81 /// 82 /// @return keys index 83 uint32_t KEY_NAME(mh_put_)(SET_TYPE *set, KEY_TYPE key, MHPutStatus *new) 84 { 85 MapHash *h = &set->h; 86 // Might rehash ahead of time if "key" already existed. But it was 87 // going to happen soon anyway. 88 if (h->n_occupied >= h->upper_bound) { 89 // If we likely were to resize soon, do it now to avoid extra rehash 90 // TODO(bfredl): we never shrink. but maybe that's fine 91 if (h->size >= h->upper_bound * 0.9) { 92 mh_realloc(h, h->n_buckets + 1); 93 } else { 94 // Just a lot of tombstones from deleted items, start all over again 95 memset(h->hash, 0, h->n_buckets * sizeof(*h->hash)); 96 h->size = h->n_occupied = 0; 97 } 98 KEY_NAME(mh_rehash_)(set); 99 } 100 101 uint32_t idx = KEY_NAME(mh_find_bucket_)(set, key, true); 102 103 if (mh_is_either(h, idx)) { 104 h->size++; 105 if (mh_is_empty(h, idx)) { 106 h->n_occupied++; 107 } 108 109 uint32_t pos = h->n_keys++; 110 if (pos >= h->keys_capacity) { 111 h->keys_capacity = MAX(h->keys_capacity * 2, 8); 112 set->keys = xrealloc(set->keys, h->keys_capacity * sizeof(KEY_TYPE)); 113 *new = kMHNewKeyRealloc; 114 } else { 115 *new = kMHNewKeyDidFit; 116 } 117 set->keys[pos] = key; 118 h->hash[idx] = pos + 1; 119 return pos; 120 } else { 121 *new = kMHExisting; 122 uint32_t pos = h->hash[idx] - 1; 123 if (!KEY_NAME(equal_)(set->keys[pos], key)) { 124 abort(); 125 } 126 return pos; 127 } 128 } 129 130 /// Deletes `*key` if found, do nothing otherwise 131 /// 132 /// @param[in, out] key modified to the value contained in the set 133 /// @return the index the item used to have in keys[] 134 /// MH_TOMBSTONE if key was not found 135 uint32_t KEY_NAME(mh_delete_)(SET_TYPE *set, KEY_TYPE *key) 136 { 137 if (set->h.size == 0) { 138 return MH_TOMBSTONE; 139 } 140 uint32_t idx = KEY_NAME(mh_find_bucket_)(set, *key, false); 141 if (idx != MH_TOMBSTONE) { 142 uint32_t k = set->h.hash[idx] - 1; 143 set->h.hash[idx] = MH_TOMBSTONE; 144 145 uint32_t last = --set->h.n_keys; 146 *key = set->keys[k]; 147 set->h.size--; 148 if (last != k) { 149 uint32_t idx2 = KEY_NAME(mh_find_bucket_)(set, set->keys[last], false); 150 if (set->h.hash[idx2] != last + 1) { 151 abort(); 152 } 153 set->h.hash[idx2] = k + 1; 154 set->keys[k] = set->keys[last]; 155 } 156 return k; 157 } 158 return MH_TOMBSTONE; 159 }