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