1 /*
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>
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <cutils/threads.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdbool.h>
25 #include <sys/types.h>
26
27 typedef struct Entry Entry;
28 struct Entry {
29 void* key;
30 int hash;
31 void* value;
32 Entry* next;
33 };
34
35 struct Hashmap {
36 Entry** buckets;
37 size_t bucketCount;
38 int (*hash)(void* key);
39 bool (*equals)(void* keyA, void* keyB);
40 mutex_t lock;
41 size_t size;
42 };
43
hashmapCreate(size_t initialCapacity,int (* hash)(void * key),bool (* equals)(void * keyA,void * keyB))44 Hashmap* hashmapCreate(size_t initialCapacity,
45 int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
46 assert(hash != NULL);
47 assert(equals != NULL);
48
49 Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
50 if (map == NULL) {
51 return NULL;
52 }
53
54 // 0.75 load factor.
55 size_t minimumBucketCount = initialCapacity * 4 / 3;
56 map->bucketCount = 1;
57 while (map->bucketCount <= minimumBucketCount) {
58 // Bucket count must be power of 2.
59 map->bucketCount <<= 1;
60 }
61
62 map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
63 if (map->buckets == NULL) {
64 free(map);
65 return NULL;
66 }
67
68 map->size = 0;
69
70 map->hash = hash;
71 map->equals = equals;
72
73 mutex_init(&map->lock);
74
75 return map;
76 }
77
78 /**
79 * Hashes the given key.
80 */
81 #ifdef __clang__
82 __attribute__((no_sanitize("integer")))
83 #endif
hashKey(Hashmap * map,void * key)84 static inline int hashKey(Hashmap* map, void* key) {
85 int h = map->hash(key);
86
87 // We apply this secondary hashing discovered by Doug Lea to defend
88 // against bad hashes.
89 h += ~(h << 9);
90 h ^= (((unsigned int) h) >> 14);
91 h += (h << 4);
92 h ^= (((unsigned int) h) >> 10);
93
94 return h;
95 }
96
hashmapSize(Hashmap * map)97 size_t hashmapSize(Hashmap* map) {
98 return map->size;
99 }
100
calculateIndex(size_t bucketCount,int hash)101 static inline size_t calculateIndex(size_t bucketCount, int hash) {
102 return ((size_t) hash) & (bucketCount - 1);
103 }
104
expandIfNecessary(Hashmap * map)105 static void expandIfNecessary(Hashmap* map) {
106 // If the load factor exceeds 0.75...
107 if (map->size > (map->bucketCount * 3 / 4)) {
108 // Start off with a 0.33 load factor.
109 size_t newBucketCount = map->bucketCount << 1;
110 Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
111 if (newBuckets == NULL) {
112 // Abort expansion.
113 return;
114 }
115
116 // Move over existing entries.
117 size_t i;
118 for (i = 0; i < map->bucketCount; i++) {
119 Entry* entry = map->buckets[i];
120 while (entry != NULL) {
121 Entry* next = entry->next;
122 size_t index = calculateIndex(newBucketCount, entry->hash);
123 entry->next = newBuckets[index];
124 newBuckets[index] = entry;
125 entry = next;
126 }
127 }
128
129 // Copy over internals.
130 free(map->buckets);
131 map->buckets = newBuckets;
132 map->bucketCount = newBucketCount;
133 }
134 }
135
hashmapLock(Hashmap * map)136 void hashmapLock(Hashmap* map) {
137 mutex_lock(&map->lock);
138 }
139
hashmapUnlock(Hashmap * map)140 void hashmapUnlock(Hashmap* map) {
141 mutex_unlock(&map->lock);
142 }
143
hashmapFree(Hashmap * map)144 void hashmapFree(Hashmap* map) {
145 size_t i;
146 for (i = 0; i < map->bucketCount; i++) {
147 Entry* entry = map->buckets[i];
148 while (entry != NULL) {
149 Entry* next = entry->next;
150 free(entry);
151 entry = next;
152 }
153 }
154 free(map->buckets);
155 mutex_destroy(&map->lock);
156 free(map);
157 }
158
159 #ifdef __clang__
160 __attribute__((no_sanitize("integer")))
161 #endif
162 /* FIXME: relies on signed integer overflow, which is undefined behavior */
hashmapHash(void * key,size_t keySize)163 int hashmapHash(void* key, size_t keySize) {
164 int h = keySize;
165 char* data = (char*) key;
166 size_t i;
167 for (i = 0; i < keySize; i++) {
168 h = h * 31 + *data;
169 data++;
170 }
171 return h;
172 }
173
createEntry(void * key,int hash,void * value)174 static Entry* createEntry(void* key, int hash, void* value) {
175 Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
176 if (entry == NULL) {
177 return NULL;
178 }
179 entry->key = key;
180 entry->hash = hash;
181 entry->value = value;
182 entry->next = NULL;
183 return entry;
184 }
185
equalKeys(void * keyA,int hashA,void * keyB,int hashB,bool (* equals)(void *,void *))186 static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
187 bool (*equals)(void*, void*)) {
188 if (keyA == keyB) {
189 return true;
190 }
191 if (hashA != hashB) {
192 return false;
193 }
194 return equals(keyA, keyB);
195 }
196
hashmapPut(Hashmap * map,void * key,void * value)197 void* hashmapPut(Hashmap* map, void* key, void* value) {
198 int hash = hashKey(map, key);
199 size_t index = calculateIndex(map->bucketCount, hash);
200
201 Entry** p = &(map->buckets[index]);
202 while (true) {
203 Entry* current = *p;
204
205 // Add a new entry.
206 if (current == NULL) {
207 *p = createEntry(key, hash, value);
208 if (*p == NULL) {
209 errno = ENOMEM;
210 return NULL;
211 }
212 map->size++;
213 expandIfNecessary(map);
214 return NULL;
215 }
216
217 // Replace existing entry.
218 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
219 void* oldValue = current->value;
220 current->value = value;
221 return oldValue;
222 }
223
224 // Move to next entry.
225 p = ¤t->next;
226 }
227 }
228
hashmapGet(Hashmap * map,void * key)229 void* hashmapGet(Hashmap* map, void* key) {
230 int hash = hashKey(map, key);
231 size_t index = calculateIndex(map->bucketCount, hash);
232
233 Entry* entry = map->buckets[index];
234 while (entry != NULL) {
235 if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
236 return entry->value;
237 }
238 entry = entry->next;
239 }
240
241 return NULL;
242 }
243
hashmapContainsKey(Hashmap * map,void * key)244 bool hashmapContainsKey(Hashmap* map, void* key) {
245 int hash = hashKey(map, key);
246 size_t index = calculateIndex(map->bucketCount, hash);
247
248 Entry* entry = map->buckets[index];
249 while (entry != NULL) {
250 if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
251 return true;
252 }
253 entry = entry->next;
254 }
255
256 return false;
257 }
258
hashmapMemoize(Hashmap * map,void * key,void * (* initialValue)(void * key,void * context),void * context)259 void* hashmapMemoize(Hashmap* map, void* key,
260 void* (*initialValue)(void* key, void* context), void* context) {
261 int hash = hashKey(map, key);
262 size_t index = calculateIndex(map->bucketCount, hash);
263
264 Entry** p = &(map->buckets[index]);
265 while (true) {
266 Entry* current = *p;
267
268 // Add a new entry.
269 if (current == NULL) {
270 *p = createEntry(key, hash, NULL);
271 if (*p == NULL) {
272 errno = ENOMEM;
273 return NULL;
274 }
275 void* value = initialValue(key, context);
276 (*p)->value = value;
277 map->size++;
278 expandIfNecessary(map);
279 return value;
280 }
281
282 // Return existing value.
283 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
284 return current->value;
285 }
286
287 // Move to next entry.
288 p = ¤t->next;
289 }
290 }
291
hashmapRemove(Hashmap * map,void * key)292 void* hashmapRemove(Hashmap* map, void* key) {
293 int hash = hashKey(map, key);
294 size_t index = calculateIndex(map->bucketCount, hash);
295
296 // Pointer to the current entry.
297 Entry** p = &(map->buckets[index]);
298 Entry* current;
299 while ((current = *p) != NULL) {
300 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
301 void* value = current->value;
302 *p = current->next;
303 free(current);
304 map->size--;
305 return value;
306 }
307
308 p = ¤t->next;
309 }
310
311 return NULL;
312 }
313
hashmapForEach(Hashmap * map,bool (* callback)(void * key,void * value,void * context),void * context)314 void hashmapForEach(Hashmap* map,
315 bool (*callback)(void* key, void* value, void* context),
316 void* context) {
317 size_t i;
318 for (i = 0; i < map->bucketCount; i++) {
319 Entry* entry = map->buckets[i];
320 while (entry != NULL) {
321 Entry *next = entry->next;
322 if (!callback(entry->key, entry->value, context)) {
323 return;
324 }
325 entry = next;
326 }
327 }
328 }
329
hashmapCurrentCapacity(Hashmap * map)330 size_t hashmapCurrentCapacity(Hashmap* map) {
331 size_t bucketCount = map->bucketCount;
332 return bucketCount * 3 / 4;
333 }
334
hashmapCountCollisions(Hashmap * map)335 size_t hashmapCountCollisions(Hashmap* map) {
336 size_t collisions = 0;
337 size_t i;
338 for (i = 0; i < map->bucketCount; i++) {
339 Entry* entry = map->buckets[i];
340 while (entry != NULL) {
341 if (entry->next != NULL) {
342 collisions++;
343 }
344 entry = entry->next;
345 }
346 }
347 return collisions;
348 }
349
hashmapIntHash(void * key)350 int hashmapIntHash(void* key) {
351 // Return the key value itself.
352 return *((int*) key);
353 }
354
hashmapIntEquals(void * keyA,void * keyB)355 bool hashmapIntEquals(void* keyA, void* keyB) {
356 int a = *((int*) keyA);
357 int b = *((int*) keyB);
358 return a == b;
359 }
360