X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Fhashmap.c;h=6f5f8204dd3dd0d2c733862edaa0b48662af3595;hb=7ad63f57b6ce7ae9e3cc19dcb441f0a4494fa3f2;hp=1c3a452899920c9ac5322863aec1c94db8f370bc;hpb=b3dcf58e283ff1bcb2c1ffacccb158d6e0c271e6;p=elogind.git diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index 1c3a45289..6f5f8204d 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -369,23 +369,31 @@ static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *ke return NULL; } -static bool 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)) - return false; + 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); if (!n) - return false; + return -ENOMEM; /* Let's use a different randomized hash key for the * extension, so that people cannot guess what we are using @@ -424,7 +432,7 @@ static bool resize_buckets(Hashmap *h) { memcpy(h->hash_key, nkey, HASH_KEY_SIZE); - return true; + return 1; } static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash) { @@ -432,7 +440,7 @@ static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash struct hashmap_entry *e; - if (resize_buckets(h)) + if (resize_buckets(h, 1) > 0) hash = bucket_hash(h, key); if (h->from_pool) @@ -795,16 +803,28 @@ int hashmap_merge(Hashmap *h, Hashmap *other) { return 0; } -void hashmap_move(Hashmap *h, Hashmap *other) { +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; +} + +int hashmap_move(Hashmap *h, Hashmap *other) { struct hashmap_entry *e, *n; assert(h); /* The same as hashmap_merge(), but every new item from other - * is moved to h. This function is guaranteed to succeed. */ + * is moved to h. */ if (!other) - return; + return 0; for (e = other->iterate_list_head; e; e = n) { unsigned h_hash, other_hash; @@ -819,6 +839,8 @@ void hashmap_move(Hashmap *h, Hashmap *other) { unlink_entry(other, e, other_hash); link_entry(h, e, h_hash); } + + return 0; } int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {