xref: /aosp_15_r20/system/core/libcutils/hashmap.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1*00c7fec1SAndroid Build Coastguard Worker /*
2*00c7fec1SAndroid Build Coastguard Worker  * Copyright (C) 2007 The Android Open Source Project
3*00c7fec1SAndroid Build Coastguard Worker  *
4*00c7fec1SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*00c7fec1SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*00c7fec1SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*00c7fec1SAndroid Build Coastguard Worker  *
8*00c7fec1SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*00c7fec1SAndroid Build Coastguard Worker  *
10*00c7fec1SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*00c7fec1SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*00c7fec1SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*00c7fec1SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*00c7fec1SAndroid Build Coastguard Worker  * limitations under the License.
15*00c7fec1SAndroid Build Coastguard Worker  */
16*00c7fec1SAndroid Build Coastguard Worker 
17*00c7fec1SAndroid Build Coastguard Worker #include <cutils/hashmap.h>
18*00c7fec1SAndroid Build Coastguard Worker 
19*00c7fec1SAndroid Build Coastguard Worker #include <assert.h>
20*00c7fec1SAndroid Build Coastguard Worker #include <errno.h>
21*00c7fec1SAndroid Build Coastguard Worker #include <pthread.h>
22*00c7fec1SAndroid Build Coastguard Worker #include <stdlib.h>
23*00c7fec1SAndroid Build Coastguard Worker #include <string.h>
24*00c7fec1SAndroid Build Coastguard Worker #include <sys/types.h>
25*00c7fec1SAndroid Build Coastguard Worker 
26*00c7fec1SAndroid Build Coastguard Worker typedef struct Entry Entry;
27*00c7fec1SAndroid Build Coastguard Worker struct Entry {
28*00c7fec1SAndroid Build Coastguard Worker     void* key;
29*00c7fec1SAndroid Build Coastguard Worker     int hash;
30*00c7fec1SAndroid Build Coastguard Worker     void* value;
31*00c7fec1SAndroid Build Coastguard Worker     Entry* next;
32*00c7fec1SAndroid Build Coastguard Worker };
33*00c7fec1SAndroid Build Coastguard Worker 
34*00c7fec1SAndroid Build Coastguard Worker struct Hashmap {
35*00c7fec1SAndroid Build Coastguard Worker     Entry** buckets;
36*00c7fec1SAndroid Build Coastguard Worker     size_t bucketCount;
37*00c7fec1SAndroid Build Coastguard Worker     int (*hash)(void* key);
38*00c7fec1SAndroid Build Coastguard Worker     bool (*equals)(void* keyA, void* keyB);
39*00c7fec1SAndroid Build Coastguard Worker     pthread_mutex_t lock;
40*00c7fec1SAndroid Build Coastguard Worker     size_t size;
41*00c7fec1SAndroid Build Coastguard Worker };
42*00c7fec1SAndroid Build Coastguard Worker 
hashmapCreate(size_t initialCapacity,int (* hash)(void * key),bool (* equals)(void * keyA,void * keyB))43*00c7fec1SAndroid Build Coastguard Worker Hashmap* hashmapCreate(size_t initialCapacity,
44*00c7fec1SAndroid Build Coastguard Worker         int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
45*00c7fec1SAndroid Build Coastguard Worker     assert(hash != NULL);
46*00c7fec1SAndroid Build Coastguard Worker     assert(equals != NULL);
47*00c7fec1SAndroid Build Coastguard Worker 
48*00c7fec1SAndroid Build Coastguard Worker     Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
49*00c7fec1SAndroid Build Coastguard Worker     if (map == NULL) {
50*00c7fec1SAndroid Build Coastguard Worker         return NULL;
51*00c7fec1SAndroid Build Coastguard Worker     }
52*00c7fec1SAndroid Build Coastguard Worker 
53*00c7fec1SAndroid Build Coastguard Worker     // 0.75 load factor.
54*00c7fec1SAndroid Build Coastguard Worker     size_t minimumBucketCount = initialCapacity * 4 / 3;
55*00c7fec1SAndroid Build Coastguard Worker     map->bucketCount = 1;
56*00c7fec1SAndroid Build Coastguard Worker     while (map->bucketCount <= minimumBucketCount) {
57*00c7fec1SAndroid Build Coastguard Worker         // Bucket count must be power of 2.
58*00c7fec1SAndroid Build Coastguard Worker         map->bucketCount <<= 1;
59*00c7fec1SAndroid Build Coastguard Worker     }
60*00c7fec1SAndroid Build Coastguard Worker 
61*00c7fec1SAndroid Build Coastguard Worker     map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
62*00c7fec1SAndroid Build Coastguard Worker     if (map->buckets == NULL) {
63*00c7fec1SAndroid Build Coastguard Worker         free(map);
64*00c7fec1SAndroid Build Coastguard Worker         return NULL;
65*00c7fec1SAndroid Build Coastguard Worker     }
66*00c7fec1SAndroid Build Coastguard Worker 
67*00c7fec1SAndroid Build Coastguard Worker     map->size = 0;
68*00c7fec1SAndroid Build Coastguard Worker 
69*00c7fec1SAndroid Build Coastguard Worker     map->hash = hash;
70*00c7fec1SAndroid Build Coastguard Worker     map->equals = equals;
71*00c7fec1SAndroid Build Coastguard Worker 
72*00c7fec1SAndroid Build Coastguard Worker     pthread_mutex_init(&map->lock, nullptr);
73*00c7fec1SAndroid Build Coastguard Worker 
74*00c7fec1SAndroid Build Coastguard Worker     return map;
75*00c7fec1SAndroid Build Coastguard Worker }
76*00c7fec1SAndroid Build Coastguard Worker 
77*00c7fec1SAndroid Build Coastguard Worker /**
78*00c7fec1SAndroid Build Coastguard Worker  * Hashes the given key.
79*00c7fec1SAndroid Build Coastguard Worker  */
80*00c7fec1SAndroid Build Coastguard Worker #ifdef __clang__
81*00c7fec1SAndroid Build Coastguard Worker __attribute__((no_sanitize("integer")))
82*00c7fec1SAndroid Build Coastguard Worker #endif
hashKey(Hashmap * map,void * key)83*00c7fec1SAndroid Build Coastguard Worker static inline int hashKey(Hashmap* map, void* key) {
84*00c7fec1SAndroid Build Coastguard Worker     int h = map->hash(key);
85*00c7fec1SAndroid Build Coastguard Worker 
86*00c7fec1SAndroid Build Coastguard Worker     // We apply this secondary hashing discovered by Doug Lea to defend
87*00c7fec1SAndroid Build Coastguard Worker     // against bad hashes.
88*00c7fec1SAndroid Build Coastguard Worker     h += ~(h << 9);
89*00c7fec1SAndroid Build Coastguard Worker     h ^= (((unsigned int) h) >> 14);
90*00c7fec1SAndroid Build Coastguard Worker     h += (h << 4);
91*00c7fec1SAndroid Build Coastguard Worker     h ^= (((unsigned int) h) >> 10);
92*00c7fec1SAndroid Build Coastguard Worker 
93*00c7fec1SAndroid Build Coastguard Worker     return h;
94*00c7fec1SAndroid Build Coastguard Worker }
95*00c7fec1SAndroid Build Coastguard Worker 
calculateIndex(size_t bucketCount,int hash)96*00c7fec1SAndroid Build Coastguard Worker static inline size_t calculateIndex(size_t bucketCount, int hash) {
97*00c7fec1SAndroid Build Coastguard Worker     return ((size_t) hash) & (bucketCount - 1);
98*00c7fec1SAndroid Build Coastguard Worker }
99*00c7fec1SAndroid Build Coastguard Worker 
expandIfNecessary(Hashmap * map)100*00c7fec1SAndroid Build Coastguard Worker static void expandIfNecessary(Hashmap* map) {
101*00c7fec1SAndroid Build Coastguard Worker     // If the load factor exceeds 0.75...
102*00c7fec1SAndroid Build Coastguard Worker     if (map->size > (map->bucketCount * 3 / 4)) {
103*00c7fec1SAndroid Build Coastguard Worker         // Start off with a 0.33 load factor.
104*00c7fec1SAndroid Build Coastguard Worker         size_t newBucketCount = map->bucketCount << 1;
105*00c7fec1SAndroid Build Coastguard Worker         Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
106*00c7fec1SAndroid Build Coastguard Worker         if (newBuckets == NULL) {
107*00c7fec1SAndroid Build Coastguard Worker             // Abort expansion.
108*00c7fec1SAndroid Build Coastguard Worker             return;
109*00c7fec1SAndroid Build Coastguard Worker         }
110*00c7fec1SAndroid Build Coastguard Worker 
111*00c7fec1SAndroid Build Coastguard Worker         // Move over existing entries.
112*00c7fec1SAndroid Build Coastguard Worker         size_t i;
113*00c7fec1SAndroid Build Coastguard Worker         for (i = 0; i < map->bucketCount; i++) {
114*00c7fec1SAndroid Build Coastguard Worker             Entry* entry = map->buckets[i];
115*00c7fec1SAndroid Build Coastguard Worker             while (entry != NULL) {
116*00c7fec1SAndroid Build Coastguard Worker                 Entry* next = entry->next;
117*00c7fec1SAndroid Build Coastguard Worker                 size_t index = calculateIndex(newBucketCount, entry->hash);
118*00c7fec1SAndroid Build Coastguard Worker                 entry->next = newBuckets[index];
119*00c7fec1SAndroid Build Coastguard Worker                 newBuckets[index] = entry;
120*00c7fec1SAndroid Build Coastguard Worker                 entry = next;
121*00c7fec1SAndroid Build Coastguard Worker             }
122*00c7fec1SAndroid Build Coastguard Worker         }
123*00c7fec1SAndroid Build Coastguard Worker 
124*00c7fec1SAndroid Build Coastguard Worker         // Copy over internals.
125*00c7fec1SAndroid Build Coastguard Worker         free(map->buckets);
126*00c7fec1SAndroid Build Coastguard Worker         map->buckets = newBuckets;
127*00c7fec1SAndroid Build Coastguard Worker         map->bucketCount = newBucketCount;
128*00c7fec1SAndroid Build Coastguard Worker     }
129*00c7fec1SAndroid Build Coastguard Worker }
130*00c7fec1SAndroid Build Coastguard Worker 
hashmapLock(Hashmap * map)131*00c7fec1SAndroid Build Coastguard Worker void hashmapLock(Hashmap* map) {
132*00c7fec1SAndroid Build Coastguard Worker     pthread_mutex_lock(&map->lock);
133*00c7fec1SAndroid Build Coastguard Worker }
134*00c7fec1SAndroid Build Coastguard Worker 
hashmapUnlock(Hashmap * map)135*00c7fec1SAndroid Build Coastguard Worker void hashmapUnlock(Hashmap* map) {
136*00c7fec1SAndroid Build Coastguard Worker     pthread_mutex_unlock(&map->lock);
137*00c7fec1SAndroid Build Coastguard Worker }
138*00c7fec1SAndroid Build Coastguard Worker 
hashmapFree(Hashmap * map)139*00c7fec1SAndroid Build Coastguard Worker void hashmapFree(Hashmap* map) {
140*00c7fec1SAndroid Build Coastguard Worker     size_t i;
141*00c7fec1SAndroid Build Coastguard Worker     for (i = 0; i < map->bucketCount; i++) {
142*00c7fec1SAndroid Build Coastguard Worker         Entry* entry = map->buckets[i];
143*00c7fec1SAndroid Build Coastguard Worker         while (entry != NULL) {
144*00c7fec1SAndroid Build Coastguard Worker             Entry* next = entry->next;
145*00c7fec1SAndroid Build Coastguard Worker             free(entry);
146*00c7fec1SAndroid Build Coastguard Worker             entry = next;
147*00c7fec1SAndroid Build Coastguard Worker         }
148*00c7fec1SAndroid Build Coastguard Worker     }
149*00c7fec1SAndroid Build Coastguard Worker     free(map->buckets);
150*00c7fec1SAndroid Build Coastguard Worker     pthread_mutex_destroy(&map->lock);
151*00c7fec1SAndroid Build Coastguard Worker     free(map);
152*00c7fec1SAndroid Build Coastguard Worker }
153*00c7fec1SAndroid Build Coastguard Worker 
154*00c7fec1SAndroid Build Coastguard Worker #ifdef __clang__
155*00c7fec1SAndroid Build Coastguard Worker __attribute__((no_sanitize("integer")))
156*00c7fec1SAndroid Build Coastguard Worker #endif
157*00c7fec1SAndroid Build Coastguard Worker /* FIXME: relies on signed integer overflow, which is undefined behavior */
hashmapHash(void * key,size_t keySize)158*00c7fec1SAndroid Build Coastguard Worker int hashmapHash(void* key, size_t keySize) {
159*00c7fec1SAndroid Build Coastguard Worker     int h = keySize;
160*00c7fec1SAndroid Build Coastguard Worker     char* data = (char*) key;
161*00c7fec1SAndroid Build Coastguard Worker     size_t i;
162*00c7fec1SAndroid Build Coastguard Worker     for (i = 0; i < keySize; i++) {
163*00c7fec1SAndroid Build Coastguard Worker         h = h * 31 + *data;
164*00c7fec1SAndroid Build Coastguard Worker         data++;
165*00c7fec1SAndroid Build Coastguard Worker     }
166*00c7fec1SAndroid Build Coastguard Worker     return h;
167*00c7fec1SAndroid Build Coastguard Worker }
168*00c7fec1SAndroid Build Coastguard Worker 
createEntry(void * key,int hash,void * value)169*00c7fec1SAndroid Build Coastguard Worker static Entry* createEntry(void* key, int hash, void* value) {
170*00c7fec1SAndroid Build Coastguard Worker     Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
171*00c7fec1SAndroid Build Coastguard Worker     if (entry == NULL) {
172*00c7fec1SAndroid Build Coastguard Worker         return NULL;
173*00c7fec1SAndroid Build Coastguard Worker     }
174*00c7fec1SAndroid Build Coastguard Worker     entry->key = key;
175*00c7fec1SAndroid Build Coastguard Worker     entry->hash = hash;
176*00c7fec1SAndroid Build Coastguard Worker     entry->value = value;
177*00c7fec1SAndroid Build Coastguard Worker     entry->next = NULL;
178*00c7fec1SAndroid Build Coastguard Worker     return entry;
179*00c7fec1SAndroid Build Coastguard Worker }
180*00c7fec1SAndroid Build Coastguard Worker 
equalKeys(void * keyA,int hashA,void * keyB,int hashB,bool (* equals)(void *,void *))181*00c7fec1SAndroid Build Coastguard Worker static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
182*00c7fec1SAndroid Build Coastguard Worker         bool (*equals)(void*, void*)) {
183*00c7fec1SAndroid Build Coastguard Worker     if (keyA == keyB) {
184*00c7fec1SAndroid Build Coastguard Worker         return true;
185*00c7fec1SAndroid Build Coastguard Worker     }
186*00c7fec1SAndroid Build Coastguard Worker     if (hashA != hashB) {
187*00c7fec1SAndroid Build Coastguard Worker         return false;
188*00c7fec1SAndroid Build Coastguard Worker     }
189*00c7fec1SAndroid Build Coastguard Worker     return equals(keyA, keyB);
190*00c7fec1SAndroid Build Coastguard Worker }
191*00c7fec1SAndroid Build Coastguard Worker 
hashmapPut(Hashmap * map,void * key,void * value)192*00c7fec1SAndroid Build Coastguard Worker void* hashmapPut(Hashmap* map, void* key, void* value) {
193*00c7fec1SAndroid Build Coastguard Worker     int hash = hashKey(map, key);
194*00c7fec1SAndroid Build Coastguard Worker     size_t index = calculateIndex(map->bucketCount, hash);
195*00c7fec1SAndroid Build Coastguard Worker 
196*00c7fec1SAndroid Build Coastguard Worker     Entry** p = &(map->buckets[index]);
197*00c7fec1SAndroid Build Coastguard Worker     while (true) {
198*00c7fec1SAndroid Build Coastguard Worker         Entry* current = *p;
199*00c7fec1SAndroid Build Coastguard Worker 
200*00c7fec1SAndroid Build Coastguard Worker         // Add a new entry.
201*00c7fec1SAndroid Build Coastguard Worker         if (current == NULL) {
202*00c7fec1SAndroid Build Coastguard Worker             *p = createEntry(key, hash, value);
203*00c7fec1SAndroid Build Coastguard Worker             if (*p == NULL) {
204*00c7fec1SAndroid Build Coastguard Worker                 errno = ENOMEM;
205*00c7fec1SAndroid Build Coastguard Worker                 return NULL;
206*00c7fec1SAndroid Build Coastguard Worker             }
207*00c7fec1SAndroid Build Coastguard Worker             map->size++;
208*00c7fec1SAndroid Build Coastguard Worker             expandIfNecessary(map);
209*00c7fec1SAndroid Build Coastguard Worker             return NULL;
210*00c7fec1SAndroid Build Coastguard Worker         }
211*00c7fec1SAndroid Build Coastguard Worker 
212*00c7fec1SAndroid Build Coastguard Worker         // Replace existing entry.
213*00c7fec1SAndroid Build Coastguard Worker         if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
214*00c7fec1SAndroid Build Coastguard Worker             void* oldValue = current->value;
215*00c7fec1SAndroid Build Coastguard Worker             current->value = value;
216*00c7fec1SAndroid Build Coastguard Worker             return oldValue;
217*00c7fec1SAndroid Build Coastguard Worker         }
218*00c7fec1SAndroid Build Coastguard Worker 
219*00c7fec1SAndroid Build Coastguard Worker         // Move to next entry.
220*00c7fec1SAndroid Build Coastguard Worker         p = &current->next;
221*00c7fec1SAndroid Build Coastguard Worker     }
222*00c7fec1SAndroid Build Coastguard Worker }
223*00c7fec1SAndroid Build Coastguard Worker 
hashmapGet(Hashmap * map,void * key)224*00c7fec1SAndroid Build Coastguard Worker void* hashmapGet(Hashmap* map, void* key) {
225*00c7fec1SAndroid Build Coastguard Worker     int hash = hashKey(map, key);
226*00c7fec1SAndroid Build Coastguard Worker     size_t index = calculateIndex(map->bucketCount, hash);
227*00c7fec1SAndroid Build Coastguard Worker 
228*00c7fec1SAndroid Build Coastguard Worker     Entry* entry = map->buckets[index];
229*00c7fec1SAndroid Build Coastguard Worker     while (entry != NULL) {
230*00c7fec1SAndroid Build Coastguard Worker         if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
231*00c7fec1SAndroid Build Coastguard Worker             return entry->value;
232*00c7fec1SAndroid Build Coastguard Worker         }
233*00c7fec1SAndroid Build Coastguard Worker         entry = entry->next;
234*00c7fec1SAndroid Build Coastguard Worker     }
235*00c7fec1SAndroid Build Coastguard Worker 
236*00c7fec1SAndroid Build Coastguard Worker     return NULL;
237*00c7fec1SAndroid Build Coastguard Worker }
238*00c7fec1SAndroid Build Coastguard Worker 
hashmapRemove(Hashmap * map,void * key)239*00c7fec1SAndroid Build Coastguard Worker void* hashmapRemove(Hashmap* map, void* key) {
240*00c7fec1SAndroid Build Coastguard Worker     int hash = hashKey(map, key);
241*00c7fec1SAndroid Build Coastguard Worker     size_t index = calculateIndex(map->bucketCount, hash);
242*00c7fec1SAndroid Build Coastguard Worker 
243*00c7fec1SAndroid Build Coastguard Worker     // Pointer to the current entry.
244*00c7fec1SAndroid Build Coastguard Worker     Entry** p = &(map->buckets[index]);
245*00c7fec1SAndroid Build Coastguard Worker     Entry* current;
246*00c7fec1SAndroid Build Coastguard Worker     while ((current = *p) != NULL) {
247*00c7fec1SAndroid Build Coastguard Worker         if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
248*00c7fec1SAndroid Build Coastguard Worker             void* value = current->value;
249*00c7fec1SAndroid Build Coastguard Worker             *p = current->next;
250*00c7fec1SAndroid Build Coastguard Worker             free(current);
251*00c7fec1SAndroid Build Coastguard Worker             map->size--;
252*00c7fec1SAndroid Build Coastguard Worker             return value;
253*00c7fec1SAndroid Build Coastguard Worker         }
254*00c7fec1SAndroid Build Coastguard Worker 
255*00c7fec1SAndroid Build Coastguard Worker         p = &current->next;
256*00c7fec1SAndroid Build Coastguard Worker     }
257*00c7fec1SAndroid Build Coastguard Worker 
258*00c7fec1SAndroid Build Coastguard Worker     return NULL;
259*00c7fec1SAndroid Build Coastguard Worker }
260*00c7fec1SAndroid Build Coastguard Worker 
hashmapForEach(Hashmap * map,bool (* callback)(void * key,void * value,void * context),void * context)261*00c7fec1SAndroid Build Coastguard Worker void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
262*00c7fec1SAndroid Build Coastguard Worker                     void* context) {
263*00c7fec1SAndroid Build Coastguard Worker     size_t i;
264*00c7fec1SAndroid Build Coastguard Worker     for (i = 0; i < map->bucketCount; i++) {
265*00c7fec1SAndroid Build Coastguard Worker         Entry* entry = map->buckets[i];
266*00c7fec1SAndroid Build Coastguard Worker         while (entry != NULL) {
267*00c7fec1SAndroid Build Coastguard Worker             Entry *next = entry->next;
268*00c7fec1SAndroid Build Coastguard Worker             if (!callback(entry->key, entry->value, context)) {
269*00c7fec1SAndroid Build Coastguard Worker                 return;
270*00c7fec1SAndroid Build Coastguard Worker             }
271*00c7fec1SAndroid Build Coastguard Worker             entry = next;
272*00c7fec1SAndroid Build Coastguard Worker         }
273*00c7fec1SAndroid Build Coastguard Worker     }
274*00c7fec1SAndroid Build Coastguard Worker }
275