blob: 57d60067cf1463132afef3fb99f1404a1c12688a [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cutils/hashmap.h>
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080018
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080019#include <assert.h>
20#include <errno.h>
Elliott Hughes9d127252018-07-13 10:54:49 -070021#include <pthread.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080022#include <stdlib.h>
23#include <string.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080024#include <sys/types.h>
25
26typedef struct Entry Entry;
27struct Entry {
28 void* key;
29 int hash;
30 void* value;
31 Entry* next;
32};
33
34struct Hashmap {
35 Entry** buckets;
36 size_t bucketCount;
37 int (*hash)(void* key);
38 bool (*equals)(void* keyA, void* keyB);
Elliott Hughes9d127252018-07-13 10:54:49 -070039 pthread_mutex_t lock;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080040 size_t size;
41};
42
43Hashmap* hashmapCreate(size_t initialCapacity,
44 int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
45 assert(hash != NULL);
46 assert(equals != NULL);
Elliott Hughes721e3eb2018-07-11 14:26:40 -070047
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080048 Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080049 if (map == NULL) {
50 return NULL;
51 }
Elliott Hughes721e3eb2018-07-11 14:26:40 -070052
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080053 // 0.75 load factor.
54 size_t minimumBucketCount = initialCapacity * 4 / 3;
55 map->bucketCount = 1;
56 while (map->bucketCount <= minimumBucketCount) {
57 // Bucket count must be power of 2.
Elliott Hughes721e3eb2018-07-11 14:26:40 -070058 map->bucketCount <<= 1;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080059 }
60
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080061 map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080062 if (map->buckets == NULL) {
63 free(map);
64 return NULL;
65 }
Elliott Hughes721e3eb2018-07-11 14:26:40 -070066
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080067 map->size = 0;
68
69 map->hash = hash;
70 map->equals = equals;
Elliott Hughes721e3eb2018-07-11 14:26:40 -070071
Elliott Hughes9d127252018-07-13 10:54:49 -070072 pthread_mutex_init(&map->lock, nullptr);
Elliott Hughes721e3eb2018-07-11 14:26:40 -070073
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080074 return map;
75}
76
77/**
78 * Hashes the given key.
79 */
Nick Kralevich73904782015-08-26 10:40:00 -070080#ifdef __clang__
81__attribute__((no_sanitize("integer")))
82#endif
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080083static inline int hashKey(Hashmap* map, void* key) {
84 int h = map->hash(key);
85
86 // We apply this secondary hashing discovered by Doug Lea to defend
87 // against bad hashes.
88 h += ~(h << 9);
89 h ^= (((unsigned int) h) >> 14);
90 h += (h << 4);
91 h ^= (((unsigned int) h) >> 10);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080092
Elliott Hughes721e3eb2018-07-11 14:26:40 -070093 return h;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080094}
95
96static inline size_t calculateIndex(size_t bucketCount, int hash) {
97 return ((size_t) hash) & (bucketCount - 1);
98}
99
100static void expandIfNecessary(Hashmap* map) {
101 // If the load factor exceeds 0.75...
102 if (map->size > (map->bucketCount * 3 / 4)) {
103 // Start off with a 0.33 load factor.
104 size_t newBucketCount = map->bucketCount << 1;
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800105 Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800106 if (newBuckets == NULL) {
107 // Abort expansion.
108 return;
109 }
Elliott Hughes721e3eb2018-07-11 14:26:40 -0700110
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800111 // Move over existing entries.
112 size_t i;
113 for (i = 0; i < map->bucketCount; i++) {
114 Entry* entry = map->buckets[i];
115 while (entry != NULL) {
116 Entry* next = entry->next;
117 size_t index = calculateIndex(newBucketCount, entry->hash);
118 entry->next = newBuckets[index];
119 newBuckets[index] = entry;
120 entry = next;
121 }
122 }
123
124 // Copy over internals.
125 free(map->buckets);
126 map->buckets = newBuckets;
127 map->bucketCount = newBucketCount;
128 }
129}
130
131void hashmapLock(Hashmap* map) {
Elliott Hughes9d127252018-07-13 10:54:49 -0700132 pthread_mutex_lock(&map->lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800133}
134
135void hashmapUnlock(Hashmap* map) {
Elliott Hughes9d127252018-07-13 10:54:49 -0700136 pthread_mutex_unlock(&map->lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800137}
138
139void hashmapFree(Hashmap* map) {
140 size_t i;
141 for (i = 0; i < map->bucketCount; i++) {
142 Entry* entry = map->buckets[i];
143 while (entry != NULL) {
144 Entry* next = entry->next;
145 free(entry);
146 entry = next;
147 }
148 }
149 free(map->buckets);
Elliott Hughes9d127252018-07-13 10:54:49 -0700150 pthread_mutex_destroy(&map->lock);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800151 free(map);
152}
153
Nick Kralevich73904782015-08-26 10:40:00 -0700154#ifdef __clang__
155__attribute__((no_sanitize("integer")))
156#endif
157/* FIXME: relies on signed integer overflow, which is undefined behavior */
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800158int hashmapHash(void* key, size_t keySize) {
159 int h = keySize;
160 char* data = (char*) key;
161 size_t i;
162 for (i = 0; i < keySize; i++) {
163 h = h * 31 + *data;
164 data++;
165 }
166 return h;
167}
168
169static Entry* createEntry(void* key, int hash, void* value) {
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800170 Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800171 if (entry == NULL) {
172 return NULL;
173 }
174 entry->key = key;
175 entry->hash = hash;
176 entry->value = value;
177 entry->next = NULL;
178 return entry;
179}
180
181static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
182 bool (*equals)(void*, void*)) {
183 if (keyA == keyB) {
184 return true;
185 }
186 if (hashA != hashB) {
187 return false;
188 }
189 return equals(keyA, keyB);
190}
191
192void* hashmapPut(Hashmap* map, void* key, void* value) {
193 int hash = hashKey(map, key);
194 size_t index = calculateIndex(map->bucketCount, hash);
195
196 Entry** p = &(map->buckets[index]);
197 while (true) {
198 Entry* current = *p;
199
200 // Add a new entry.
201 if (current == NULL) {
202 *p = createEntry(key, hash, value);
203 if (*p == NULL) {
204 errno = ENOMEM;
205 return NULL;
206 }
207 map->size++;
208 expandIfNecessary(map);
209 return NULL;
210 }
211
212 // Replace existing entry.
213 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
214 void* oldValue = current->value;
215 current->value = value;
216 return oldValue;
217 }
218
219 // Move to next entry.
220 p = &current->next;
221 }
222}
223
224void* hashmapGet(Hashmap* map, void* key) {
225 int hash = hashKey(map, key);
226 size_t index = calculateIndex(map->bucketCount, hash);
227
228 Entry* entry = map->buckets[index];
229 while (entry != NULL) {
230 if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
231 return entry->value;
232 }
233 entry = entry->next;
234 }
235
236 return NULL;
237}
238
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800239void* hashmapRemove(Hashmap* map, void* key) {
240 int hash = hashKey(map, key);
241 size_t index = calculateIndex(map->bucketCount, hash);
242
243 // Pointer to the current entry.
244 Entry** p = &(map->buckets[index]);
245 Entry* current;
246 while ((current = *p) != NULL) {
247 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
248 void* value = current->value;
249 *p = current->next;
250 free(current);
251 map->size--;
252 return value;
253 }
254
255 p = &current->next;
256 }
257
258 return NULL;
259}
260
Elliott Hughes721e3eb2018-07-11 14:26:40 -0700261void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
262 void* context) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800263 size_t i;
264 for (i = 0; i < map->bucketCount; i++) {
265 Entry* entry = map->buckets[i];
266 while (entry != NULL) {
Dima Zavin4fab9ac2011-03-24 11:12:00 -0700267 Entry *next = entry->next;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800268 if (!callback(entry->key, entry->value, context)) {
269 return;
270 }
Dima Zavin4fab9ac2011-03-24 11:12:00 -0700271 entry = next;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800272 }
273 }
274}