| 175 | |
| 176 | |
| 177 | __hot |
| 178 | ConcurrentMap::result ConcurrentMap::insert(slice key, value_t value, hash_t hash) { |
| 179 | assert_precondition(key); |
| 180 | const char *allocedKey = nullptr; |
| 181 | int i = indexOfHash(hash); |
| 182 | while (true) { |
| 183 | retry: |
| 184 | Entry current = _entries[i]; |
| 185 | switch (current.keyOffset) { |
| 186 | case kEmptyKeyOffset: |
| 187 | case kDeletedKeyOffset: { |
| 188 | // Found an empty or deleted entry to use. First allocate the string: |
| 189 | if (!allocedKey) { |
| 190 | if (_count >= _capacity) |
| 191 | return {}; // Hash table overflow |
| 192 | allocedKey = allocKey(key); |
| 193 | if (!allocedKey) |
| 194 | return {}; // Key-strings overflow |
| 195 | } |
| 196 | Entry newEntry = {keyToOffset(allocedKey), value}; |
| 197 | // Try to store my new entry, if another thread didn't beat me to it: |
| 198 | if (_usuallyFalse(!_entries[i].compareAndSwap(current, newEntry))) { |
| 199 | // I was beaten to it; retry (at the same index, |
| 200 | // in case CAS was a false negative) |
| 201 | goto retry; |
| 202 | } |
| 203 | // Success! |
| 204 | ++_count; |
| 205 | assert(_count <= _capacity); |
| 206 | return {slice(allocedKey, key.size), value}; |
| 207 | } |
| 208 | default: |
| 209 | if (auto keyPtr = offsetToKey(current.keyOffset); equalKeys(keyPtr, key)) { |
| 210 | // Key already exists in table. Deallocate any string I allocated: |
| 211 | freeKey(allocedKey); |
| 212 | return {slice(keyPtr, key.size), current.value}; |
| 213 | } |
| 214 | break; |
| 215 | } |
| 216 | i = wrap(i + 1); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | |
| 221 | bool ConcurrentMap::remove(slice key, hash_t hash) { |
nothing calls this directly
no test coverage detected