Add load factor option

It's now possible to set the load factor at compile time using
the HASHMAP_LOAD_FACTOR define:

   cc -DHASHMAP_LOAD_FACTOR=0.75 ...

or by using the hashmap_set_load_factor function

  hashmap_set_load_factor(map, 0.75);

The load factor should be in the range of 50% to 95%.
pull/40/head v0.8.0
tidwall 2 years ago
parent 84d0d3bf75
commit f5b39e9e4a

@ -9,8 +9,12 @@
#include <stddef.h>
#include "hashmap.h"
#define GROW_AT 0.60
#define SHRINK_AT 0.10
#define GROW_AT 0.60 /* 60% */
#define SHRINK_AT 0.10 /* 10% */
#ifndef HASHMAP_LOAD_FACTOR
#define HASHMAP_LOAD_FACTOR GROW_AT
#endif
static void *(*__malloc)(size_t) = NULL;
static void *(*__realloc)(void *, size_t) = NULL;
@ -48,6 +52,7 @@ struct hashmap {
size_t mask;
size_t growat;
size_t shrinkat;
uint8_t loadfactor;
uint8_t growpower;
bool oom;
void *buckets;
@ -59,6 +64,20 @@ void hashmap_set_grow_by_power(struct hashmap *map, size_t power) {
map->growpower = power < 1 ? 1 : power > 16 ? 16 : power;
}
static double clamp_load_factor(double factor, double default_factor) {
// Check for NaN and clamp between 50% and 90%
return factor != factor ? default_factor :
factor < 0.50 ? 0.50 :
factor > 0.95 ? 0.95 :
factor;
}
void hashmap_set_load_factor(struct hashmap *map, double factor) {
factor = clamp_load_factor(factor, map->loadfactor / 100.0);
map->loadfactor = factor * 100;
map->growat = map->nbuckets * (map->loadfactor / 100.0);
}
static struct bucket *bucket_at0(void *buckets, size_t bucketsz, size_t i) {
return (struct bucket*)(((char*)buckets)+(bucketsz*i));
}
@ -79,6 +98,7 @@ static uint64_t get_hash(struct hashmap *map, const void *key) {
return clip_hash(map->hash(key, map->seed0, map->seed1));
}
// 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),
@ -101,7 +121,6 @@ struct hashmap *hashmap_new_with_allocator(void *(*_malloc)(size_t),
}
cap = ncap;
}
// printf("%d\n", (int)cap);
size_t bucketsz = sizeof(struct bucket) + elsize;
while (bucketsz & (sizeof(uintptr_t)-1)) {
bucketsz++;
@ -133,8 +152,9 @@ struct hashmap *hashmap_new_with_allocator(void *(*_malloc)(size_t),
}
memset(map->buckets, 0, map->bucketsz*map->nbuckets);
map->growpower = 1;
map->growat = map->nbuckets*GROW_AT;
map->shrinkat = map->nbuckets*SHRINK_AT;
map->loadfactor = clamp_load_factor(HASHMAP_LOAD_FACTOR, GROW_AT) * 100;
map->growat = map->nbuckets * (map->loadfactor / 100.0);
map->shrinkat = map->nbuckets * SHRINK_AT;
map->malloc = _malloc;
map->realloc = _realloc;
map->free = _free;
@ -199,8 +219,8 @@ void hashmap_clear(struct hashmap *map, bool update_cap) {
}
memset(map->buckets, 0, map->bucketsz*map->nbuckets);
map->mask = map->nbuckets-1;
map->growat = map->nbuckets*0.75;
map->shrinkat = map->nbuckets*0.10;
map->growat = map->nbuckets * (map->loadfactor / 100.0) ;
map->shrinkat = map->nbuckets * SHRINK_AT;
}
static bool resize0(struct hashmap *map, size_t new_cap) {
@ -252,7 +272,7 @@ const void *hashmap_set_with_hash(struct hashmap *map, const void *item,
{
hash = clip_hash(hash);
map->oom = false;
if (map->count == map->growat) {
if (map->count >= map->growat) {
if (!resize(map, map->nbuckets*(1<<map->growpower))) {
map->oom = true;
return NULL;

@ -45,6 +45,7 @@ const void *hashmap_get_with_hash(struct hashmap *map, const void *key, uint64_t
const void *hashmap_delete_with_hash(struct hashmap *map, const void *key, uint64_t hash);
const void *hashmap_set_with_hash(struct hashmap *map, const void *item, uint64_t hash);
void hashmap_set_grow_by_power(struct hashmap *map, size_t power);
void hashmap_set_load_factor(struct hashmap *map, double load_factor);
// DEPRECATED: use `hashmap_new_with_allocator`

Loading…
Cancel
Save