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 <pthread.h>
22 #include <stdlib.h>
23 #include <string.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 pthread_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 = static_cast<Hashmap*>(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 = static_cast<Entry**>(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 pthread_mutex_init(&map->lock, nullptr);
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
calculateIndex(size_t bucketCount,int hash)96 static inline size_t calculateIndex(size_t bucketCount, int hash) {
97 return ((size_t) hash) & (bucketCount - 1);
98 }
99
expandIfNecessary(Hashmap * map)100 static 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;
105 Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
106 if (newBuckets == NULL) {
107 // Abort expansion.
108 return;
109 }
110
111 // 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
hashmapLock(Hashmap * map)131 void hashmapLock(Hashmap* map) {
132 pthread_mutex_lock(&map->lock);
133 }
134
hashmapUnlock(Hashmap * map)135 void hashmapUnlock(Hashmap* map) {
136 pthread_mutex_unlock(&map->lock);
137 }
138
hashmapFree(Hashmap * map)139 void 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);
150 pthread_mutex_destroy(&map->lock);
151 free(map);
152 }
153
154 #ifdef __clang__
155 __attribute__((no_sanitize("integer")))
156 #endif
157 /* FIXME: relies on signed integer overflow, which is undefined behavior */
hashmapHash(void * key,size_t keySize)158 int 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
createEntry(void * key,int hash,void * value)169 static Entry* createEntry(void* key, int hash, void* value) {
170 Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
171 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
equalKeys(void * keyA,int hashA,void * keyB,int hashB,bool (* equals)(void *,void *))181 static 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
hashmapPut(Hashmap * map,void * key,void * value)192 void* 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 = ¤t->next;
221 }
222 }
223
hashmapGet(Hashmap * map,void * key)224 void* 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
hashmapRemove(Hashmap * map,void * key)239 void* 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 = ¤t->next;
256 }
257
258 return NULL;
259 }
260
hashmapForEach(Hashmap * map,bool (* callback)(void * key,void * value,void * context),void * context)261 void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
262 void* context) {
263 size_t i;
264 for (i = 0; i < map->bucketCount; i++) {
265 Entry* entry = map->buckets[i];
266 while (entry != NULL) {
267 Entry *next = entry->next;
268 if (!callback(entry->key, entry->value, context)) {
269 return;
270 }
271 entry = next;
272 }
273 }
274 }
275