• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &current->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 = &current->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 = &current->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