hashmap: introduce hashmap_reserve()

With the current hashmap implementation that uses chaining, placing a
reservation can serve two purposes:
 - To optimize putting of entries if the number of entries to put is
   known. The reservation allocates buckets, so later resizing can be
   avoided.
 - To avoid having very long bucket chains after using
   hashmap_move(_one).

In an alternative hashmap implementation it will serve an additional
purpose:
 - To guarantee a subsequent hashmap_move(_one) will not fail with
   -ENOMEM (this never happens in the current implementation).

Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
This commit is contained in:
Michal Schmidt 2014-10-25 18:18:26 -04:00 committed by Anthony G. Basile
parent ac2d134b8c
commit 4531818f12
4 changed files with 33 additions and 6 deletions

View File

@ -276,18 +276,26 @@ static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *ke
return NULL;
}
static int resize_buckets(Hashmap *h) {
static int resize_buckets(Hashmap *h, unsigned entries_add) {
struct hashmap_entry **n, *i;
unsigned m;
unsigned m, new_n_entries, new_n_buckets;
uint8_t nkey[HASH_KEY_SIZE];
assert(h);
if (_likely_(h->n_entries*4 < h->n_buckets*3))
new_n_entries = h->n_entries + entries_add;
/* overflow? */
if (_unlikely_(new_n_entries < entries_add || new_n_entries > UINT_MAX / 4))
return -ENOMEM;
new_n_buckets = new_n_entries * 4 / 3;
if (_likely_(new_n_buckets <= h->n_buckets))
return 0;
/* Increase by four */
m = (h->n_entries+1)*4-1;
/* Increase by four at least */
m = MAX((h->n_entries+1)*4-1, new_n_buckets);
/* If we hit OOM we simply risk packed hashmaps... */
n = new0(struct hashmap_entry*, m);
@ -339,7 +347,7 @@ static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash
struct hashmap_entry *e;
if (resize_buckets(h) > 0)
if (resize_buckets(h, 1) > 0)
hash = bucket_hash(h, key);
if (h->from_pool)
@ -476,6 +484,18 @@ unsigned hashmap_size(Hashmap *h) {
return h->n_entries;
}
int hashmap_reserve(Hashmap *h, unsigned entries_add) {
int r;
assert(h);
r = resize_buckets(h, entries_add);
if (r < 0)
return r;
return 0;
}
char **hashmap_get_strv(Hashmap *h) {
char **sv;
Iterator it;

View File

@ -65,6 +65,7 @@ int hashmap_put(Hashmap *h, const void *key, void *value);
void *hashmap_get(Hashmap *h, const void *key);
void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
bool hashmap_contains(Hashmap *h, const void *key);
int hashmap_reserve(Hashmap *h, unsigned entries_add);
unsigned hashmap_size(Hashmap *h) _pure_;

View File

@ -50,3 +50,7 @@ bool set_contains(Set *s, void *value) {
void *set_iterate(Set *s, Iterator *i) {
return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
}
int set_reserve(Set *s, unsigned entries_add) {
return hashmap_reserve(MAKE_HASHMAP(s), entries_add);
}

View File

@ -37,6 +37,8 @@ int set_put(Set *s, void *value);
void *set_get(Set *s, void *value);
bool set_contains(Set *s, void *value);
int set_reserve(Set *s, unsigned entries_add);
void *set_iterate(Set *s, Iterator *i);
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);