diff --git a/hashmap.c b/hashmap.c index d51d976..845978b 100644 --- a/hashmap.c +++ b/hashmap.c @@ -10,11 +10,9 @@ #include "hashmap.h" static void *(*_malloc)(size_t) = NULL; +static void *(*_realloc)(void *, size_t) = NULL; static void (*_free)(void *) = NULL; -#define hmmalloc (_malloc?_malloc:malloc) -#define hmfree (_free?_free:free) - // hashmap_set_allocator allows for configuring a custom allocator for // all hashmap library operations. This function, if needed, should be called // only once at startup and a prior to calling hashmap_new(). @@ -36,6 +34,9 @@ struct bucket { // hashmap is an open addressed hash map using robinhood hashing. struct hashmap { + void *(*malloc)(size_t); + void *(*realloc)(void *, size_t); + void (*free)(void *); bool oom; size_t elsize; size_t cap; @@ -67,22 +68,13 @@ static uint64_t get_hash(struct hashmap *map, void *key) { return map->hash(key, map->seed0, map->seed1) << 16 >> 16; } -// hashmap_new returns a new hash map. -// Param `elsize` is the size of each element in the tree. Every element that -// is inserted, deleted, or retrieved will be this size. -// Param `cap` is the default lower capacity of the hashmap. Setting this to -// zero will default to 16. -// Params `seed0` and `seed1` are optional seed values that are passed to the -// following `hash` function. These can be any value you wish but it's often -// best to use randomly generated values. -// Param `hash` is a function that generates a hash value for an item. It's -// important that you provide a good hash function, otherwise it will perform -// poorly or be vulnerable to Denial-of-service attacks. This implementation -// comes with two helper functions `hashmap_sip()` and `hashmap_murmur()`. -// Param `compare` is a function that compares items in the tree. See the -// qsort stdlib function for an example of how this function works. -// The hashmap must be freed with hashmap_free(). -struct hashmap *hashmap_new(size_t elsize, size_t cap, +// hashmap_new_with_allocator returns a new hash map using a custom allocator. +// See hashmap_new for more information information +struct hashmap *hashmap_new_with_allocator( + void *(*_malloc)(size_t), + void *(*_realloc)(void*, size_t), + void (*_free)(void*), + size_t elsize, size_t cap, uint64_t seed0, uint64_t seed1, uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1), @@ -90,6 +82,9 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap, void *udata), void *udata) { + _malloc = _malloc ? _malloc : malloc; + _realloc = _realloc ? _realloc : realloc; + _free = _free ? _free : free; int ncap = 16; if (cap < ncap) { cap = ncap; @@ -105,7 +100,7 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap, } // hashmap + spare + edata size_t size = sizeof(struct hashmap)+bucketsz*2; - struct hashmap *map = hmmalloc(size); + struct hashmap *map = _malloc(size); if (!map) { return NULL; } @@ -122,15 +117,50 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap, map->cap = cap; map->nbuckets = cap; map->mask = map->nbuckets-1; - map->buckets = hmmalloc(map->bucketsz*map->nbuckets); + map->buckets = _malloc(map->bucketsz*map->nbuckets); if (!map->buckets) { - hmfree(map); + _free(map); return NULL; } memset(map->buckets, 0, map->bucketsz*map->nbuckets); map->growat = map->nbuckets*0.75; map->shrinkat = map->nbuckets*0.10; - return map; + map->malloc = _malloc; + map->realloc = _realloc; + map->free = _free; + return map; +} + + +// hashmap_new returns a new hash map. +// Param `elsize` is the size of each element in the tree. Every element that +// is inserted, deleted, or retrieved will be this size. +// Param `cap` is the default lower capacity of the hashmap. Setting this to +// zero will default to 16. +// Params `seed0` and `seed1` are optional seed values that are passed to the +// following `hash` function. These can be any value you wish but it's often +// best to use randomly generated values. +// Param `hash` is a function that generates a hash value for an item. It's +// important that you provide a good hash function, otherwise it will perform +// poorly or be vulnerable to Denial-of-service attacks. This implementation +// comes with two helper functions `hashmap_sip()` and `hashmap_murmur()`. +// Param `compare` is a function that compares items in the tree. See the +// qsort stdlib function for an example of how this function works. +// The hashmap must be freed with hashmap_free(). +struct hashmap *hashmap_new(size_t elsize, size_t cap, + uint64_t seed0, uint64_t seed1, + uint64_t (*hash)(const void *item, + uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, + void *udata), + void *udata) +{ + return hashmap_new_with_allocator( + (_malloc?_malloc:malloc), + (_realloc?_realloc:realloc), + (_free?_free:free), + elsize, cap, seed0, seed1, hash, compare, udata + ); } // hashmap_clear quickly clears the map. @@ -142,9 +172,9 @@ void hashmap_clear(struct hashmap *map, bool update_cap) { if (update_cap) { map->cap = map->nbuckets; } else if (map->nbuckets != map->cap) { - void *new_buckets = hmmalloc(map->bucketsz*map->cap); + void *new_buckets = map->malloc(map->bucketsz*map->cap); if (new_buckets) { - hmfree(map->buckets); + map->free(map->buckets); map->buckets = new_buckets; } map->nbuckets = map->cap; @@ -185,13 +215,13 @@ static bool resize(struct hashmap *map, size_t new_cap) { entry->dib += 1; } } - hmfree(map->buckets); + map->free(map->buckets); map->buckets = map2->buckets; map->nbuckets = map2->nbuckets; map->mask = map2->mask; map->growat = map2->growat; map->shrinkat = map2->shrinkat; - hmfree(map2); + map->free(map2); return true; } @@ -329,8 +359,8 @@ size_t hashmap_count(struct hashmap *map) { // hashmap_free frees the hash map void hashmap_free(struct hashmap *map) { if (!map) return; - hmfree(map->buckets); - hmfree(map); + map->free(map->buckets); + map->free(map); } // hashmap_oom returns true if the last hashmap_set() call failed due to the diff --git a/hashmap.h b/hashmap.h index 6fb083d..9d83906 100644 --- a/hashmap.h +++ b/hashmap.h @@ -18,6 +18,17 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap, int (*compare)(const void *a, const void *b, void *udata), void *udata); +struct hashmap *hashmap_new_with_allocator( + void *(*malloc)(size_t), + void *(*realloc)(void *, size_t), + void (*free)(void*), + size_t elsize, size_t cap, + uint64_t seed0, uint64_t seed1, + uint64_t (*hash)(const void *item, + uint64_t seed0, uint64_t seed1), + int (*compare)(const void *a, const void *b, + void *udata), + void *udata); void hashmap_free(struct hashmap *map); void hashmap_clear(struct hashmap *map, bool update_cap); size_t hashmap_count(struct hashmap *map); @@ -28,10 +39,14 @@ void *hashmap_delete(struct hashmap *map, void *item); void *hashmap_probe(struct hashmap *map, uint64_t position); bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata); -void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*)); + uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1); uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1); + +// DEPRECATED: use `hashmap_new_with_allocator` +void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*)); + #endif