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