123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <cutils/hashmap.h>
- #include <assert.h>
- #include <errno.h>
- #include <pthread.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- typedef struct Entry Entry;
- struct Entry {
- void* key;
- int hash;
- void* value;
- Entry* next;
- };
- struct Hashmap {
- Entry** buckets;
- size_t bucketCount;
- int (*hash)(void* key);
- bool (*equals)(void* keyA, void* keyB);
- pthread_mutex_t lock;
- size_t size;
- };
- Hashmap* hashmapCreate(size_t initialCapacity,
- int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
- assert(hash != NULL);
- assert(equals != NULL);
- Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
- if (map == NULL) {
- return NULL;
- }
- // 0.75 load factor.
- size_t minimumBucketCount = initialCapacity * 4 / 3;
- map->bucketCount = 1;
- while (map->bucketCount <= minimumBucketCount) {
- // Bucket count must be power of 2.
- map->bucketCount <<= 1;
- }
- map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
- if (map->buckets == NULL) {
- free(map);
- return NULL;
- }
- map->size = 0;
- map->hash = hash;
- map->equals = equals;
- pthread_mutex_init(&map->lock, nullptr);
- return map;
- }
- /**
- * Hashes the given key.
- */
- #ifdef __clang__
- __attribute__((no_sanitize("integer")))
- #endif
- static inline int hashKey(Hashmap* map, void* key) {
- int h = map->hash(key);
- // We apply this secondary hashing discovered by Doug Lea to defend
- // against bad hashes.
- h += ~(h << 9);
- h ^= (((unsigned int) h) >> 14);
- h += (h << 4);
- h ^= (((unsigned int) h) >> 10);
- return h;
- }
- static inline size_t calculateIndex(size_t bucketCount, int hash) {
- return ((size_t) hash) & (bucketCount - 1);
- }
- static void expandIfNecessary(Hashmap* map) {
- // If the load factor exceeds 0.75...
- if (map->size > (map->bucketCount * 3 / 4)) {
- // Start off with a 0.33 load factor.
- size_t newBucketCount = map->bucketCount << 1;
- Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));
- if (newBuckets == NULL) {
- // Abort expansion.
- return;
- }
- // Move over existing entries.
- size_t i;
- for (i = 0; i < map->bucketCount; i++) {
- Entry* entry = map->buckets[i];
- while (entry != NULL) {
- Entry* next = entry->next;
- size_t index = calculateIndex(newBucketCount, entry->hash);
- entry->next = newBuckets[index];
- newBuckets[index] = entry;
- entry = next;
- }
- }
- // Copy over internals.
- free(map->buckets);
- map->buckets = newBuckets;
- map->bucketCount = newBucketCount;
- }
- }
- void hashmapLock(Hashmap* map) {
- pthread_mutex_lock(&map->lock);
- }
- void hashmapUnlock(Hashmap* map) {
- pthread_mutex_unlock(&map->lock);
- }
- void hashmapFree(Hashmap* map) {
- size_t i;
- for (i = 0; i < map->bucketCount; i++) {
- Entry* entry = map->buckets[i];
- while (entry != NULL) {
- Entry* next = entry->next;
- free(entry);
- entry = next;
- }
- }
- free(map->buckets);
- pthread_mutex_destroy(&map->lock);
- free(map);
- }
- #ifdef __clang__
- __attribute__((no_sanitize("integer")))
- #endif
- /* FIXME: relies on signed integer overflow, which is undefined behavior */
- int hashmapHash(void* key, size_t keySize) {
- int h = keySize;
- char* data = (char*) key;
- size_t i;
- for (i = 0; i < keySize; i++) {
- h = h * 31 + *data;
- data++;
- }
- return h;
- }
- static Entry* createEntry(void* key, int hash, void* value) {
- Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));
- if (entry == NULL) {
- return NULL;
- }
- entry->key = key;
- entry->hash = hash;
- entry->value = value;
- entry->next = NULL;
- return entry;
- }
- static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
- bool (*equals)(void*, void*)) {
- if (keyA == keyB) {
- return true;
- }
- if (hashA != hashB) {
- return false;
- }
- return equals(keyA, keyB);
- }
- void* hashmapPut(Hashmap* map, void* key, void* value) {
- int hash = hashKey(map, key);
- size_t index = calculateIndex(map->bucketCount, hash);
- Entry** p = &(map->buckets[index]);
- while (true) {
- Entry* current = *p;
- // Add a new entry.
- if (current == NULL) {
- *p = createEntry(key, hash, value);
- if (*p == NULL) {
- errno = ENOMEM;
- return NULL;
- }
- map->size++;
- expandIfNecessary(map);
- return NULL;
- }
- // Replace existing entry.
- if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
- void* oldValue = current->value;
- current->value = value;
- return oldValue;
- }
- // Move to next entry.
- p = ¤t->next;
- }
- }
- void* hashmapGet(Hashmap* map, void* key) {
- int hash = hashKey(map, key);
- size_t index = calculateIndex(map->bucketCount, hash);
- Entry* entry = map->buckets[index];
- while (entry != NULL) {
- if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
- return entry->value;
- }
- entry = entry->next;
- }
- return NULL;
- }
- void* hashmapRemove(Hashmap* map, void* key) {
- int hash = hashKey(map, key);
- size_t index = calculateIndex(map->bucketCount, hash);
- // Pointer to the current entry.
- Entry** p = &(map->buckets[index]);
- Entry* current;
- while ((current = *p) != NULL) {
- if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
- void* value = current->value;
- *p = current->next;
- free(current);
- map->size--;
- return value;
- }
- p = ¤t->next;
- }
- return NULL;
- }
- void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
- void* context) {
- size_t i;
- for (i = 0; i < map->bucketCount; i++) {
- Entry* entry = map->buckets[i];
- while (entry != NULL) {
- Entry *next = entry->next;
- if (!callback(entry->key, entry->value, context)) {
- return;
- }
- entry = next;
- }
- }
- }
|