diff options
author | Michal Schmidt <mschmidt@redhat.com> | 2014-10-25 18:18:26 -0400 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2014-10-25 18:18:26 -0400 |
commit | 4531818f12221fdedba9f6f4913d22228628328c (patch) | |
tree | 60bed7efe713cce5a840914938bef998e0c9ae0c | |
parent | ac2d134b8c27629754463d505b2aaab3c99c71c6 (diff) |
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>
-rw-r--r-- | src/shared/hashmap.c | 32 | ||||
-rw-r--r-- | src/shared/hashmap.h | 1 | ||||
-rw-r--r-- | src/shared/set.c | 4 | ||||
-rw-r--r-- | src/shared/set.h | 2 |
4 files changed, 33 insertions, 6 deletions
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index e965167258..fa4edf11c6 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -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; diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 53b04eb033..22bda37437 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -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_; diff --git a/src/shared/set.c b/src/shared/set.c index 4b8c76feca..087d874598 100644 --- a/src/shared/set.c +++ b/src/shared/set.c @@ -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); +} diff --git a/src/shared/set.h b/src/shared/set.h index f1111d1bc8..ae3090a09f 100644 --- a/src/shared/set.h +++ b/src/shared/set.h @@ -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); |