Added hashmap_new_with_allocator

Deprecated hashmap_set_allocator

See #6 for more information
pull/13/head v0.4.0
tidwall 4 years ago
parent 735eafed75
commit 9fb4b295a0

@ -10,11 +10,9 @@
#include "hashmap.h" #include "hashmap.h"
static void *(*_malloc)(size_t) = NULL; static void *(*_malloc)(size_t) = NULL;
static void *(*_realloc)(void *, size_t) = NULL;
static void (*_free)(void *) = 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 // hashmap_set_allocator allows for configuring a custom allocator for
// all hashmap library operations. This function, if needed, should be called // all hashmap library operations. This function, if needed, should be called
// only once at startup and a prior to calling hashmap_new(). // 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. // hashmap is an open addressed hash map using robinhood hashing.
struct hashmap { struct hashmap {
void *(*malloc)(size_t);
void *(*realloc)(void *, size_t);
void (*free)(void *);
bool oom; bool oom;
size_t elsize; size_t elsize;
size_t cap; 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; return map->hash(key, map->seed0, map->seed1) << 16 >> 16;
} }
// hashmap_new returns a new hash map. // hashmap_new_with_allocator returns a new hash map using a custom allocator.
// Param `elsize` is the size of each element in the tree. Every element that // See hashmap_new for more information information
// is inserted, deleted, or retrieved will be this size. struct hashmap *hashmap_new_with_allocator(
// Param `cap` is the default lower capacity of the hashmap. Setting this to void *(*_malloc)(size_t),
// zero will default to 16. void *(*_realloc)(void*, size_t),
// Params `seed0` and `seed1` are optional seed values that are passed to the void (*_free)(void*),
// following `hash` function. These can be any value you wish but it's often size_t elsize, size_t cap,
// 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 seed0, uint64_t seed1,
uint64_t (*hash)(const void *item, uint64_t (*hash)(const void *item,
uint64_t seed0, uint64_t seed1), uint64_t seed0, uint64_t seed1),
@ -90,6 +82,9 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap,
void *udata), void *udata),
void *udata) void *udata)
{ {
_malloc = _malloc ? _malloc : malloc;
_realloc = _realloc ? _realloc : realloc;
_free = _free ? _free : free;
int ncap = 16; int ncap = 16;
if (cap < ncap) { if (cap < ncap) {
cap = ncap; cap = ncap;
@ -105,7 +100,7 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap,
} }
// hashmap + spare + edata // hashmap + spare + edata
size_t size = sizeof(struct hashmap)+bucketsz*2; size_t size = sizeof(struct hashmap)+bucketsz*2;
struct hashmap *map = hmmalloc(size); struct hashmap *map = _malloc(size);
if (!map) { if (!map) {
return NULL; return NULL;
} }
@ -122,15 +117,50 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap,
map->cap = cap; map->cap = cap;
map->nbuckets = cap; map->nbuckets = cap;
map->mask = map->nbuckets-1; map->mask = map->nbuckets-1;
map->buckets = hmmalloc(map->bucketsz*map->nbuckets); map->buckets = _malloc(map->bucketsz*map->nbuckets);
if (!map->buckets) { if (!map->buckets) {
hmfree(map); _free(map);
return NULL; return NULL;
} }
memset(map->buckets, 0, map->bucketsz*map->nbuckets); memset(map->buckets, 0, map->bucketsz*map->nbuckets);
map->growat = map->nbuckets*0.75; map->growat = map->nbuckets*0.75;
map->shrinkat = map->nbuckets*0.10; 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. // hashmap_clear quickly clears the map.
@ -142,9 +172,9 @@ void hashmap_clear(struct hashmap *map, bool update_cap) {
if (update_cap) { if (update_cap) {
map->cap = map->nbuckets; map->cap = map->nbuckets;
} else if (map->nbuckets != map->cap) { } 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) { if (new_buckets) {
hmfree(map->buckets); map->free(map->buckets);
map->buckets = new_buckets; map->buckets = new_buckets;
} }
map->nbuckets = map->cap; map->nbuckets = map->cap;
@ -185,13 +215,13 @@ static bool resize(struct hashmap *map, size_t new_cap) {
entry->dib += 1; entry->dib += 1;
} }
} }
hmfree(map->buckets); map->free(map->buckets);
map->buckets = map2->buckets; map->buckets = map2->buckets;
map->nbuckets = map2->nbuckets; map->nbuckets = map2->nbuckets;
map->mask = map2->mask; map->mask = map2->mask;
map->growat = map2->growat; map->growat = map2->growat;
map->shrinkat = map2->shrinkat; map->shrinkat = map2->shrinkat;
hmfree(map2); map->free(map2);
return true; return true;
} }
@ -329,8 +359,8 @@ size_t hashmap_count(struct hashmap *map) {
// hashmap_free frees the hash map // hashmap_free frees the hash map
void hashmap_free(struct hashmap *map) { void hashmap_free(struct hashmap *map) {
if (!map) return; if (!map) return;
hmfree(map->buckets); map->free(map->buckets);
hmfree(map); map->free(map);
} }
// hashmap_oom returns true if the last hashmap_set() call failed due to the // hashmap_oom returns true if the last hashmap_set() call failed due to the

@ -18,6 +18,17 @@ struct hashmap *hashmap_new(size_t elsize, size_t cap,
int (*compare)(const void *a, const void *b, int (*compare)(const void *a, const void *b,
void *udata), void *udata),
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_free(struct hashmap *map);
void hashmap_clear(struct hashmap *map, bool update_cap); void hashmap_clear(struct hashmap *map, bool update_cap);
size_t hashmap_count(struct hashmap *map); 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); void *hashmap_probe(struct hashmap *map, uint64_t position);
bool hashmap_scan(struct hashmap *map, bool hashmap_scan(struct hashmap *map,
bool (*iter)(const void *item, void *udata), void *udata); 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 hashmap_sip(const void *data, size_t len,
uint64_t seed0, uint64_t seed1); uint64_t seed0, uint64_t seed1);
uint64_t hashmap_murmur(const void *data, size_t len, uint64_t hashmap_murmur(const void *data, size_t len,
uint64_t seed0, uint64_t seed1); uint64_t seed0, uint64_t seed1);
// DEPRECATED: use `hashmap_new_with_allocator`
void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*));
#endif #endif

Loading…
Cancel
Save