blob: bf36c3093fc6df5839c3cb836462059caefa3817 [file] [log] [blame]
/**
*Licensed to the Apache Software Foundation (ASF) under one
*or more contributor license agreements. See the NOTICE file
*distributed with this work for additional information
*regarding copyright ownership. The ASF licenses this file
*to you 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 "celix_string_hash_map.h"
#include "celix_long_hash_map.h"
#include "celix_hash_map_private.h"
#include "celix_hash_map_internal.h"
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <assert.h>
#include <stdint.h>
#include "celix_utils.h"
#include "celix_err.h"
#include "celix_stdlib_cleanup.h"
#define CELIX_HASHMAP_DEFAULT_INITIAL_CAPACITY 16
#define CELIX_HASHMAP_DEFAULT_MAX_LOAD_FACTOR 5.0
#define CELIX_HASHMAP_CAPACITY_INCREASE_FACTOR 2
#define CELIX_HASHMAP_MAXIMUM_CAPACITY (INT32_MAX/10)
#define CELIX_HASHMAP_MAXIMUM_INCREASE_VALUE (1024*10)
#define CELIX_HASHMAP_HASH_PRIME 1610612741
union celix_hash_map_key {
const char* strKey;
long longKey;
};
struct celix_hash_map_entry {
celix_hash_map_key_t key;
celix_hash_map_value_t value;
celix_hash_map_entry_t* next;
unsigned int hash;
};
struct celix_hash_map {
celix_hash_map_entry_t** buckets;
unsigned int bucketsSize; //nr of buckets
unsigned int size; //nr of total entries
double maxLoadFactor;
celix_hash_map_key_type_e keyType;
void (*simpleRemovedCallback)(void* value);
void* removedCallbackData;
void (*removedStringEntryCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue);
void (*removedStringKeyCallback)(void* data, char* key);
void (*removedLongEntryCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue);
bool storeKeysWeakly;
//statistics
size_t resizeCount;
};
struct celix_string_hash_map {
celix_hash_map_t genericMap;
};
struct celix_long_hash_map {
celix_hash_map_t genericMap;
};
// hash1 (original)
//static unsigned int celix_longHashMap_hash(long key) {
// return key ^ (key >> (sizeof(key)*8/2));
//}
// hash2 (using long hash from deprecated maphash)
//static unsigned int celix_longHashMap_hash(long key) {
// long h = key;
// h += ~(h << 9);
// h ^= ((h >> 14) | (h << 18)); /* >>> */
// h += (h << 4);
// h ^= ((h >> 10) | (h << 22)); /* >>> */
// return h;
//}
// hash3 (using a prime)
static unsigned int celix_longHashMap_hash(long key) {
return (key ^ (key >> (sizeof(key)*8/2)) * CELIX_HASHMAP_HASH_PRIME);
}
/**
* @brief Check if hash map needs to be resized if a extra entry is added.
*/
static bool celix_hashMap_needsResize(const celix_hash_map_t* map) {
double loadFactor = (double)(map->size +1) / (double)map->bucketsSize;
return loadFactor > map->maxLoadFactor;
}
static unsigned int celix_hashMap_indexFor(unsigned int h, unsigned int length) {
return h % length;
}
/**
* @brief get entry from hash map. Long key is used if strKey is NULL.
*/
static celix_hash_map_entry_t* celix_hashMap_getEntry(const celix_hash_map_t* map, const char* strKey, long longKey) {
unsigned int hash = strKey ? celix_utils_stringHash(strKey) : celix_longHashMap_hash(longKey);
unsigned int index = celix_hashMap_indexFor(hash, map->bucketsSize);
if (strKey) {
for (celix_hash_map_entry_t* entry = map->buckets[index]; entry != NULL; entry = entry->next) {
if (entry->hash == hash && celix_utils_stringEquals(strKey, entry->key.strKey)) {
return entry;
}
}
} else {
for (celix_hash_map_entry_t* entry = map->buckets[index]; entry != NULL; entry = entry->next) {
if (entry->hash == hash && longKey == entry->key.longKey) {
return entry;
}
}
}
return NULL;
}
static void* celix_hashMap_get(const celix_hash_map_t* map, const char* strKey, long longKey) {
celix_hash_map_entry_t* entry = celix_hashMap_getEntry(map, strKey, longKey);
if (entry != NULL) {
return entry->value.ptrValue;
}
return NULL;
}
static long celix_hashMap_getLong(const celix_hash_map_t* map, const char* strKey, long longKey, long fallbackValue) {
celix_hash_map_entry_t* entry = celix_hashMap_getEntry(map, strKey, longKey);
if (entry != NULL) {
return entry->value.longValue;
}
return fallbackValue;
}
static double celix_hashMap_getDouble(const celix_hash_map_t* map, const char* strKey, long longKey, double fallbackValue) {
celix_hash_map_entry_t* entry = celix_hashMap_getEntry(map, strKey, longKey);
if (entry != NULL) {
return entry->value.doubleValue;
}
return fallbackValue;
}
static bool celix_hashMap_getBool(const celix_hash_map_t* map, const char* strKey, long longKey, bool fallbackValue) {
celix_hash_map_entry_t* entry = celix_hashMap_getEntry(map, strKey, longKey);
if (entry != NULL) {
return entry->value.boolValue;
}
return fallbackValue;
}
bool celix_hashMap_hasKey(const celix_hash_map_t* map, const char* strKey, long longKey) {
return celix_hashMap_getEntry(map, strKey, longKey) != NULL;
}
celix_status_t celix_hashMap_resize(celix_hash_map_t *map) {
if (map->bucketsSize >= CELIX_HASHMAP_MAXIMUM_CAPACITY) {
return CELIX_SUCCESS;
}
size_t newCapacity = (size_t)floor((double)map->bucketsSize * CELIX_HASHMAP_CAPACITY_INCREASE_FACTOR);
if (map->bucketsSize > CELIX_HASHMAP_MAXIMUM_INCREASE_VALUE) {
//after a certain point, only increase with CELIX_HASHMAP_MAXIMUM_INCREASE_VALUE instead of a factor
newCapacity = map->bucketsSize + CELIX_HASHMAP_MAXIMUM_INCREASE_VALUE;
}
assert(newCapacity > map->bucketsSize); //hash map can only grow
celix_hash_map_entry_t **newBuf = realloc(map->buckets, newCapacity * sizeof(celix_hash_map_entry_t *));
if (!newBuf) {
celix_err_push("Cannot realloc memory for hash map");
return CELIX_ENOMEM;
}
map->buckets = newBuf;
//clear newly added mem
for (unsigned int i = map->bucketsSize; i < newCapacity; ++i) {
map->buckets[i] = NULL;
}
//reinsert old entries
for (unsigned i = 0; i < map->bucketsSize; i++) {
celix_hash_map_entry_t *entry = map->buckets[i];
if (entry != NULL) {
map->buckets[i] = NULL;
do {
celix_hash_map_entry_t *next = entry->next;
unsigned int bucketIndex = celix_hashMap_indexFor(entry->hash, newCapacity);
entry->next = map->buckets[bucketIndex];
map->buckets[bucketIndex] = entry;
entry = next;
} while (entry != NULL);
}
}
//update bucketSize to new capacity
map->bucketsSize = newCapacity;
map->resizeCount += 1;
return CELIX_SUCCESS;
}
static void celix_hashMap_callRemovedCallback(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) {
if (map->simpleRemovedCallback) {
map->simpleRemovedCallback(removedEntry->value.ptrValue);
} else if (map->removedLongEntryCallback) {
map->removedLongEntryCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value);
} else if (map->removedStringEntryCallback) {
map->removedStringEntryCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value);
}
}
static void celix_hashMap_destroyRemovedKey(celix_hash_map_t* map, char* removedKey) {
if (map->removedStringKeyCallback) {
map->removedStringKeyCallback(map->removedCallbackData, removedKey);
}
if (!map->storeKeysWeakly) {
free(removedKey);
}
}
static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) {
celix_hashMap_callRemovedCallback(map, removedEntry);
free(removedEntry);
}
celix_status_t celix_hashMap_addEntry(celix_hash_map_t* map, const celix_hash_map_key_t* key, const celix_hash_map_value_t* value) {
//resize (if needed) first, so that if allocation fails, no entry is yet created
if (celix_hashMap_needsResize(map)) {
celix_status_t status = celix_hashMap_resize(map);
if (status != CELIX_SUCCESS) {
return status;
}
}
bool isStringKey = map->keyType == CELIX_HASH_MAP_STRING_KEY;
unsigned int hash = isStringKey ? celix_utils_stringHash(key->strKey) : celix_longHashMap_hash(key->longKey);
unsigned int bucketIndex = celix_hashMap_indexFor(hash, map->bucketsSize);
celix_hash_map_entry_t* entry = map->buckets[bucketIndex];
celix_hash_map_entry_t* newEntry = malloc(sizeof(*newEntry));
if (!newEntry) {
celix_err_push("Cannot allocate memory for hash map entry");
return CELIX_ENOMEM;
}
newEntry->hash = hash;
if (isStringKey) {
newEntry->key.strKey = map->storeKeysWeakly ? key->strKey : celix_utils_strdup(key->strKey);
} else {
newEntry->key.longKey = key->longKey;
}
memcpy(&newEntry->value, value, sizeof(*value));
newEntry->next = entry;
map->buckets[bucketIndex] = newEntry;
map->size += 1;
return CELIX_SUCCESS;
}
/**
* @brief Put the value in the map. If long hash is used, strKey should be NULL.
*/
static celix_status_t celix_hashMap_putValue(celix_hash_map_t* map, const char* strKey, long longKey, const celix_hash_map_value_t* value) {
celix_hash_map_key_t key;
if (strKey) {
key.strKey = strKey;
} else {
key.longKey = longKey;
}
celix_hash_map_entry_t* entryFound = celix_hashMap_getEntry(map, strKey, longKey);
if (entryFound) {
//replace value
celix_hashMap_callRemovedCallback(map, entryFound);
memcpy(&entryFound->value, value, sizeof(*value));
return CELIX_SUCCESS;
}
//new entry
celix_status_t status = celix_hashMap_addEntry(map, &key, value);
return status;
}
static celix_status_t celix_hashMap_put(celix_hash_map_t* map, const char* strKey, long longKey, void* v) {
celix_hash_map_value_t value;
memset(&value, 0, sizeof(value));
value.ptrValue = v;
return celix_hashMap_putValue(map, strKey, longKey, &value);
}
static celix_status_t celix_hashMap_putLong(celix_hash_map_t* map, const char* strKey, long longKey, long v) {
celix_hash_map_value_t value;
memset(&value, 0, sizeof(value));
value.longValue = v;
return celix_hashMap_putValue(map, strKey, longKey, &value);
}
static celix_status_t celix_hashMap_putDouble(celix_hash_map_t* map, const char* strKey, long longKey, double v) {
celix_hash_map_value_t value;
memset(&value, 0, sizeof(value));
value.doubleValue = v;
return celix_hashMap_putValue(map, strKey, longKey, &value);
}
static celix_status_t celix_hashMap_putBool(celix_hash_map_t* map, const char* strKey, long longKey, bool v) {
celix_hash_map_value_t value;
memset(&value, 0, sizeof(value));
value.boolValue = v;
return celix_hashMap_putValue(map, strKey, longKey, &value);
}
/**
* @brief Remove entry from hash map. If long hash is used, strKey should be NULL.
*/
static bool celix_hashMap_remove(celix_hash_map_t* map, const char* strKey, long longKey) {
unsigned int hash = strKey ? celix_utils_stringHash(strKey) : celix_longHashMap_hash(longKey);
unsigned int index = celix_hashMap_indexFor(hash, map->bucketsSize);
celix_hash_map_entry_t* visit = map->buckets[index];
celix_hash_map_entry_t* removedEntry = NULL;
celix_hash_map_entry_t* prev = NULL;
while (visit != NULL) {
if (visit->hash == hash) {
bool equals = strKey ? celix_utils_stringEquals(strKey, visit->key.strKey) : longKey == visit->key.longKey;
if (equals) {
// hash & equals -> found entry
map->size -= 1;
if (map->buckets[index] == visit) {
// current entry is first entry in bucket, set next to first entry in bucket
map->buckets[index] = visit->next;
} else {
// entry is in between, update link of prev to next entry
prev->next = visit->next;
}
removedEntry = visit;
break;
}
}
prev = visit;
visit = visit->next;
}
if (removedEntry != NULL) {
char* removedKey = NULL;
if (strKey) {
removedKey = (char*)removedEntry->key.strKey;
}
celix_hashMap_destroyRemovedEntry(map, removedEntry);
if (removedKey) {
celix_hashMap_destroyRemovedKey(map, removedKey);
}
return true;
}
return false;
}
celix_status_t celix_hashMap_init(
celix_hash_map_t* map,
celix_hash_map_key_type_e keyType,
unsigned int initialCapacity,
double maxLoadFactor) {
map->maxLoadFactor = maxLoadFactor;
map->size = 0;
map->bucketsSize = initialCapacity;
map->keyType = keyType;
map->simpleRemovedCallback = NULL;
map->removedCallbackData = NULL;
map->removedLongEntryCallback = NULL;
map->removedStringEntryCallback = NULL;
map->removedStringKeyCallback = NULL;
map->storeKeysWeakly = false;
map->resizeCount = 0;
map->buckets = calloc(initialCapacity, sizeof(celix_hash_map_entry_t*));
return map->buckets == NULL ? CELIX_ENOMEM : CELIX_SUCCESS;
}
static void celix_hashMap_clear(celix_hash_map_t* map) {
for (unsigned int i = 0; i < map->bucketsSize; i++) {
celix_hash_map_entry_t* entry = map->buckets[i];
while (entry != NULL) {
celix_hash_map_entry_t* removedEntry = entry;
entry = entry->next;
char* removedKey = NULL;
if (map->keyType == CELIX_HASH_MAP_STRING_KEY) {
removedKey = (char*)removedEntry->key.strKey;
}
celix_hashMap_destroyRemovedEntry(map, removedEntry);
if (removedKey) {
celix_hashMap_destroyRemovedKey(map, removedKey);
}
}
map->buckets[i] = NULL;
}
map->size = 0;
}
static celix_hash_map_entry_t* celix_hashMap_firstEntry(const celix_hash_map_t* map) {
celix_hash_map_entry_t* entry = NULL;
for (unsigned int index = 0; index < map->bucketsSize; ++index) {
entry = map->buckets[index];
if (entry != NULL) {
break;
}
}
return entry;
}
static celix_hash_map_entry_t* celix_hashMap_nextEntry(const celix_hash_map_t* map, celix_hash_map_entry_t* entry) {
if (entry == NULL) {
//end entry, just return NULL
return NULL;
}
celix_hash_map_entry_t* next = NULL;
if (entry != NULL) {
if (entry->next != NULL) {
next = entry->next;
} else {
unsigned int index = celix_hashMap_indexFor(entry->hash, map->bucketsSize) + 1; //next index
while (index < map->bucketsSize) {
next = map->buckets[index++];
if (next != NULL) {
break;
}
}
}
}
return next;
}
celix_string_hash_map_t* celix_stringHashMap_createWithOptions(const celix_string_hash_map_create_options_t* opts) {
celix_autofree celix_string_hash_map_t* map = calloc(1, sizeof(*map));
if (!map) {
celix_err_push("Cannot allocate memory for hash map");
return NULL;
}
unsigned int cap = opts->initialCapacity > 0 ? opts->initialCapacity : CELIX_HASHMAP_DEFAULT_INITIAL_CAPACITY;
double fac = opts->maxLoadFactor > 0 ? opts->maxLoadFactor : CELIX_HASHMAP_DEFAULT_MAX_LOAD_FACTOR;
celix_status_t status = celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_STRING_KEY, cap, fac);
if (status != CELIX_SUCCESS) {
celix_err_push("Cannot initialize hash map");
return NULL;
}
map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback;
map->genericMap.removedCallbackData = opts->removedCallbackData;
map->genericMap.removedStringEntryCallback = opts->removedCallback;
map->genericMap.removedStringKeyCallback = opts->removedKeyCallback;
map->genericMap.storeKeysWeakly = opts->storeKeysWeakly;
return celix_steal_ptr(map);
}
celix_string_hash_map_t* celix_stringHashMap_create() {
celix_string_hash_map_create_options_t opts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS;
return celix_stringHashMap_createWithOptions(&opts);
}
celix_long_hash_map_t* celix_longHashMap_createWithOptions(const celix_long_hash_map_create_options_t* opts) {
celix_autofree celix_long_hash_map_t* map = calloc(1, sizeof(*map));
if (!map) {
celix_err_push("Cannot allocate memory for hash map");
return NULL;
}
unsigned int cap = opts->initialCapacity > 0 ? opts->initialCapacity : CELIX_HASHMAP_DEFAULT_INITIAL_CAPACITY;
double fac = opts->maxLoadFactor > 0 ? opts->maxLoadFactor : CELIX_HASHMAP_DEFAULT_MAX_LOAD_FACTOR;
celix_status_t status = celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_LONG_KEY, cap, fac);
if (status != CELIX_SUCCESS) {
celix_err_push("Cannot initialize hash map");
return NULL;
}
map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback;
map->genericMap.removedCallbackData = opts->removedCallbackData;
map->genericMap.removedLongEntryCallback = opts->removedCallback;
map->genericMap.storeKeysWeakly = false;
return celix_steal_ptr(map);
}
celix_long_hash_map_t* celix_longHashMap_create() {
celix_long_hash_map_create_options_t opts = CELIX_EMPTY_LONG_HASH_MAP_CREATE_OPTIONS;
return celix_longHashMap_createWithOptions(&opts);
}
void celix_stringHashMap_destroy(celix_string_hash_map_t* map) {
if (map != NULL) {
celix_hashMap_clear(&map->genericMap);
free(map->genericMap.buckets);
free(map);
}
}
void celix_longHashMap_destroy(celix_long_hash_map_t* map) {
if (map != NULL) {
celix_hashMap_clear(&map->genericMap);
free(map->genericMap.buckets);
free(map);
}
}
size_t celix_stringHashMap_size(const celix_string_hash_map_t* map) {
return map->genericMap.size;
}
size_t celix_longHashMap_size(const celix_long_hash_map_t* map) {
return map->genericMap.size;
}
void* celix_stringHashMap_get(const celix_string_hash_map_t* map, const char* key) {
return celix_hashMap_get(&map->genericMap, key, 0);
}
void* celix_longHashMap_get(const celix_long_hash_map_t* map, long key) {
return celix_hashMap_get(&map->genericMap, NULL, key);
}
long celix_stringHashMap_getLong(const celix_string_hash_map_t* map, const char* key, long fallbackValue) {
return celix_hashMap_getLong(&map->genericMap, key, 0, fallbackValue);
}
long celix_longHashMap_getLong(const celix_long_hash_map_t* map, long key, long fallbackValue) {
return celix_hashMap_getLong(&map->genericMap, NULL, key, fallbackValue);
}
double celix_stringHashMap_getDouble(const celix_string_hash_map_t* map, const char* key, double fallbackValue) {
return celix_hashMap_getDouble(&map->genericMap, key, 0, fallbackValue);
}
double celix_longHashMap_getDouble(const celix_long_hash_map_t* map, long key, double fallbackValue) {
return celix_hashMap_getDouble(&map->genericMap, NULL, key, fallbackValue);
}
bool celix_stringHashMap_getBool(const celix_string_hash_map_t* map, const char* key, bool fallbackValue) {
return celix_hashMap_getBool(&map->genericMap, key, 0, fallbackValue);
}
bool celix_longHashMap_getBool(const celix_long_hash_map_t* map, long key, bool fallbackValue) {
return celix_hashMap_getBool(&map->genericMap, NULL, key, fallbackValue);
}
celix_status_t celix_stringHashMap_put(celix_string_hash_map_t* map, const char* key, void* value) {
return celix_hashMap_put(&map->genericMap, key, 0, value);
}
celix_status_t celix_longHashMap_put(celix_long_hash_map_t* map, long key, void* value) {
return celix_hashMap_put(&map->genericMap, NULL, key, value);
}
celix_status_t celix_stringHashMap_putLong(celix_string_hash_map_t* map, const char* key, long value) {
return celix_hashMap_putLong(&map->genericMap, key, 0, value);
}
celix_status_t celix_longHashMap_putLong(celix_long_hash_map_t* map, long key, long value) {
return celix_hashMap_putLong(&map->genericMap, NULL, key, value);
}
celix_status_t celix_stringHashMap_putDouble(celix_string_hash_map_t* map, const char* key, double value) {
return celix_hashMap_putDouble(&map->genericMap, key, 0, value);
}
celix_status_t celix_longHashMap_putDouble(celix_long_hash_map_t* map, long key, double value) {
return celix_hashMap_putDouble(&map->genericMap, NULL, key, value);
}
celix_status_t celix_stringHashMap_putBool(celix_string_hash_map_t* map, const char* key, bool value) {
return celix_hashMap_putBool(&map->genericMap, key, 0, value);
}
celix_status_t celix_longHashMap_putBool(celix_long_hash_map_t* map, long key, bool value) {
return celix_hashMap_putBool(&map->genericMap, NULL, key, value);
}
bool celix_stringHashMap_hasKey(const celix_string_hash_map_t* map, const char* key) {
return celix_hashMap_hasKey(&map->genericMap, key, 0);
}
bool celix_longHashMap_hasKey(const celix_long_hash_map_t* map, long key) {
return celix_hashMap_hasKey(&map->genericMap, NULL, key);
}
bool celix_stringHashMap_remove(celix_string_hash_map_t* map, const char* key) {
return celix_hashMap_remove(&map->genericMap, key, 0);
}
bool celix_longHashMap_remove(celix_long_hash_map_t* map, long key) {
return celix_hashMap_remove(&map->genericMap, NULL, key);
}
void celix_stringHashMap_clear(celix_string_hash_map_t* map) {
celix_hashMap_clear(&map->genericMap);
}
void celix_longHashMap_clear(celix_long_hash_map_t* map) {
celix_hashMap_clear(&map->genericMap);
}
static bool celix_hashMap_equals(const celix_hash_map_t* map1, const celix_hash_map_t* map2) {
if (map1 == map2) {
return true;
}
if (map1->size != map2->size) {
return false;
}
for (celix_hash_map_entry_t* entry = celix_hashMap_firstEntry(map1); entry != NULL; entry = celix_hashMap_nextEntry(map1, entry)) {
const char* strKey = map1->keyType == CELIX_HASH_MAP_STRING_KEY ? entry->key.strKey : NULL;
long longKey = map1->keyType == CELIX_HASH_MAP_LONG_KEY ? entry->key.longKey : 0;
celix_hash_map_entry_t* entryMap2 = celix_hashMap_getEntry(map2, strKey, longKey);
//note using memcmp, so for void* values the pointer value is compared, not the value itself.
if (entryMap2 == NULL || memcmp(&entryMap2->value, &entry->value, sizeof(entryMap2->value)) != 0) {
return false;
}
}
return true;
}
bool celix_stringHashMap_equals(const celix_string_hash_map_t* map1, const celix_string_hash_map_t* map2) {
if (map1 == NULL && map2 == NULL) {
return true;
}
if (map1 == NULL || map2 == NULL) {
return false;
}
return celix_hashMap_equals(&map1->genericMap, &map2->genericMap);
}
bool celix_longHashMap_equals(const celix_long_hash_map_t* map1, const celix_long_hash_map_t* map2) {
if (map1 == NULL && map2 == NULL) {
return true;
}
if (map1 == NULL || map2 == NULL) {
return false;
}
if (map1->genericMap.size != map2->genericMap.size) {
return false;
}
return celix_hashMap_equals(&map1->genericMap, &map2->genericMap);
}
celix_string_hash_map_iterator_t celix_stringHashMap_begin(const celix_string_hash_map_t* map) {
celix_string_hash_map_iterator_t iter;
memset(&iter, 0, sizeof(iter));
iter._internal[0] = (void*)&map->genericMap;
if (map->genericMap.size > 0) {
celix_hash_map_entry_t *entry = celix_hashMap_firstEntry(&map->genericMap);
iter._internal[1] = entry;
iter.value = entry->value;
iter.key = entry->key.strKey;
}
return iter;
}
celix_long_hash_map_iterator_t celix_longHashMap_begin(const celix_long_hash_map_t* map) {
celix_long_hash_map_iterator_t iter;
memset(&iter, 0, sizeof(iter));
iter._internal[0] = (void*)&map->genericMap;
if (map->genericMap.size > 0) {
celix_hash_map_entry_t *entry = celix_hashMap_firstEntry(&map->genericMap);
iter._internal[1] = entry;
iter.value = entry->value;
iter.key = entry->key.longKey;
}
return iter;
}
celix_string_hash_map_iterator_t celix_stringHashMap_end(const celix_string_hash_map_t* map) {
celix_string_hash_map_iterator_t iter;
iter._internal[0] = (void*)&map->genericMap;
iter._internal[1] = NULL;
iter.key = "";
memset(&iter.value, 0, sizeof(iter.value));
return iter;
}
celix_long_hash_map_iterator_t celix_longHashMap_end(const celix_long_hash_map_t* map) {
celix_long_hash_map_iterator_t iter;
iter._internal[0] = (void*)&map->genericMap;
iter._internal[1] = NULL;
iter.key = 0L;
memset(&iter.value, 0, sizeof(iter.value));
return iter;
}
bool celix_stringHashMapIterator_isEnd(const celix_string_hash_map_iterator_t* iter) {
return iter->_internal[1] == NULL; //check if entry is NULL
}
bool celix_longHashMapIterator_isEnd(const celix_long_hash_map_iterator_t* iter) {
return iter->_internal[1] == NULL; //check if entry is NULL
}
void celix_stringHashMapIterator_next(celix_string_hash_map_iterator_t* iter) {
const celix_hash_map_t* map = iter->_internal[0];
celix_hash_map_entry_t *entry = iter->_internal[1];
entry = celix_hashMap_nextEntry(map, entry);
if (entry) {
iter->_internal[1] = entry;
iter->key = entry->key.strKey;
iter->value = entry->value;
} else {
iter->_internal[1] = NULL;
iter->key = "";
memset(&iter->value, 0, sizeof(iter->value));
}
}
void celix_longHashMapIterator_next(celix_long_hash_map_iterator_t* iter) {
const celix_hash_map_t* map = iter->_internal[0];
celix_hash_map_entry_t *entry = iter->_internal[1];
entry = celix_hashMap_nextEntry(map, entry);
if (entry) {
iter->_internal[1] = entry;
iter->key = entry->key.longKey;
iter->value = entry->value;
} else {
iter->_internal[1] = NULL;
iter->key = 0L;
memset(&iter->value, 0, sizeof(iter->value));
}
}
bool celix_stringHashMapIterator_equals(
const celix_string_hash_map_iterator_t* iterator,
const celix_string_hash_map_iterator_t* other) {
return iterator->_internal[0] == other->_internal[0] /* same map */ &&
iterator->_internal[1] == other->_internal[1] /* same entry */;
}
bool celix_longHashMapIterator_equals(
const celix_long_hash_map_iterator_t* iterator,
const celix_long_hash_map_iterator_t* other) {
return iterator->_internal[0] == other->_internal[0] /* same map */ &&
iterator->_internal[1] == other->_internal[1] /* same entry */;
}
void celix_stringHashMapIterator_remove(celix_string_hash_map_iterator_t* iter) {
celix_hash_map_t* map = iter->_internal[0];
celix_hash_map_entry_t *entry = iter->_internal[1];
const char* key = entry->key.strKey;
celix_stringHashMapIterator_next(iter);
celix_hashMap_remove(map, key, 0);
}
void celix_longHashMapIterator_remove(celix_long_hash_map_iterator_t* iter) {
celix_hash_map_t* map = iter->_internal[0];
celix_hash_map_entry_t *entry = iter->_internal[1];
long key = entry->key.longKey;
celix_longHashMapIterator_next(iter);
celix_hashMap_remove(map, NULL, key);
}
static int celix_hashMap_nrOfEntriesInBucket(const celix_hash_map_t* map, int bucketIndex) {
int cnt = 0;
celix_hash_map_entry_t* entry = map->buckets[bucketIndex];
while (entry != NULL) {
cnt += 1;
entry = entry->next;
}
return cnt;
}
static celix_hash_map_statistics_t celix_hashMap_getStatistics(const celix_hash_map_t* map) {
celix_hash_map_statistics_t stats;
stats.nrOfEntries = map->size;
stats.nrOfBuckets = map->bucketsSize;
stats.resizeCount = map->resizeCount;
double avg = (double)map->size / (double)map->bucketsSize; //note avg == load factor
double stdDev = 0.0;
for (int i = 0; i < map->bucketsSize; ++i) {
int entriesInBucket = celix_hashMap_nrOfEntriesInBucket(map, i);
stdDev += (entriesInBucket - avg) * (entriesInBucket - avg);
}
stdDev = stdDev / map->bucketsSize;
stdDev = sqrt(stdDev);
stats.averageNrOfEntriesPerBucket = avg;
stats.stdDeviationNrOfEntriesPerBucket = stdDev;
return stats;
}
celix_hash_map_statistics_t celix_longHashMap_getStatistics(const celix_long_hash_map_t* map) {
return celix_hashMap_getStatistics(&map->genericMap);
}
celix_hash_map_statistics_t celix_stringHashMap_getStatistics(const celix_string_hash_map_t* map) {
return celix_hashMap_getStatistics(&map->genericMap);
}