• 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 #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 = &current->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 = &current->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 = &current->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