X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Fhashmap.c;h=8225b8ebccd5e742eea1f90b42091c1c48238254;hb=dd0124f6cb6ff7da1ce8be23ec7e1137fe15958d;hp=65b7b741284bdcb5c0ad673cf6ae6b24110d66ea;hpb=29804cc1e0f37ee34301530fd7f1eb8550be464e;p=elogind.git diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index 65b7b7412..8225b8ebc 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -39,8 +39,7 @@ struct hashmap_entry { }; struct Hashmap { - hash_func_t hash_func; - compare_func_t compare_func; + const struct hash_ops *hash_ops; struct hashmap_entry *iterate_list_head, *iterate_list_tail; @@ -141,6 +140,11 @@ int string_compare_func(const void *a, const void *b) { return strcmp(a, b); } +const struct hash_ops string_hash_ops = { + .hash = string_hash_func, + .compare = string_compare_func +}; + unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { uint64_t u; siphash24((uint8_t*) &u, &p, sizeof(p), hash_key); @@ -151,6 +155,11 @@ int trivial_compare_func(const void *a, const void *b) { return a < b ? -1 : (a > b ? 1 : 0); } +const struct hash_ops trivial_hash_ops = { + .hash = trivial_hash_func, + .compare = trivial_compare_func +}; + unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { uint64_t u; siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key); @@ -164,8 +173,33 @@ int uint64_compare_func(const void *_a, const void *_b) { return a < b ? -1 : (a > b ? 1 : 0); } +const struct hash_ops uint64_hash_ops = { + .hash = uint64_hash_func, + .compare = uint64_compare_func +}; + +#if SIZEOF_DEV_T != 8 +unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { + uint64_t u; + siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key); + return (unsigned long) u; +} + +int devt_compare_func(const void *_a, const void *_b) { + dev_t a, b; + a = *(const dev_t*) _a; + b = *(const dev_t*) _b; + return a < b ? -1 : (a > b ? 1 : 0); +} + +const struct hash_ops devt_hash_ops = { + .hash = devt_hash_func, + .compare = devt_compare_func +}; +#endif + static unsigned bucket_hash(Hashmap *h, const void *p) { - return (unsigned) (h->hash_func(p, h->hash_key) % h->n_buckets); + return (unsigned) (h->hash_ops->hash(p, h->hash_key) % h->n_buckets); } static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { @@ -187,7 +221,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { memcpy(hash_key, current, sizeof(current)); } -Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { +Hashmap *hashmap_new(const struct hash_ops *hash_ops) { bool b; Hashmap *h; size_t size; @@ -209,8 +243,7 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { return NULL; } - h->hash_func = hash_func ? hash_func : trivial_hash_func; - h->compare_func = compare_func ? compare_func : trivial_compare_func; + h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; h->n_buckets = INITIAL_N_BUCKETS; h->n_entries = 0; @@ -225,7 +258,7 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { return h; } -int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) { +int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops) { Hashmap *q; assert(h); @@ -233,7 +266,7 @@ int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t if (*h) return 0; - q = hashmap_new(hash_func, compare_func); + q = hashmap_new(hash_ops); if (!q) return -ENOMEM; @@ -391,7 +424,7 @@ static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *ke assert(hash < h->n_buckets); for (e = h->buckets[hash]; e; e = e->bucket_next) - if (h->compare_func(e->key, key) == 0) + if (h->hash_ops->compare(e->key, key) == 0) return e; return NULL; @@ -423,7 +456,7 @@ static bool resize_buckets(Hashmap *h) { for (i = h->iterate_list_head; i; i = i->iterate_next) { unsigned long old_bucket, new_bucket; - old_bucket = h->hash_func(i->key, h->hash_key) % h->n_buckets; + old_bucket = h->hash_ops->hash(i->key, h->hash_key) % h->n_buckets; /* First, drop from old bucket table */ if (i->bucket_next) @@ -435,7 +468,7 @@ static bool resize_buckets(Hashmap *h) { h->buckets[old_bucket] = i->bucket_next; /* Then, add to new backet table */ - new_bucket = h->hash_func(i->key, nkey) % m; + new_bucket = h->hash_ops->hash(i->key, nkey) % m; i->bucket_next = n[new_bucket]; i->bucket_previous = NULL; @@ -455,19 +488,10 @@ static bool resize_buckets(Hashmap *h) { return true; } -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; - - assert(h); +static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash) { + /* For when we know no such entry exists yet */ - hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (e) { - if (e->value == value) - return 0; - return -EEXIST; - } + struct hashmap_entry *e; if (resize_buckets(h)) hash = bucket_hash(h, key); @@ -488,6 +512,23 @@ int hashmap_put(Hashmap *h, const void *key, void *value) { return 1; } +int hashmap_put(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = bucket_hash(h, key); + e = hash_scan(h, hash, key); + if (e) { + if (e->value == value) + return 0; + return -EEXIST; + } + + return __hashmap_put(h, key, value, hash); +} + int hashmap_replace(Hashmap *h, const void *key, void *value) { struct hashmap_entry *e; unsigned hash; @@ -502,7 +543,7 @@ int hashmap_replace(Hashmap *h, const void *key, void *value) { return 0; } - return hashmap_put(h, key, value); + return __hashmap_put(h, key, value, hash); } int hashmap_update(Hashmap *h, const void *key, void *value) { @@ -582,6 +623,34 @@ void* hashmap_remove(Hashmap *h, const void *key) { return data; } +void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) { + struct hashmap_entry *e; + unsigned hash; + void *data; + + if (!h) { + if (rkey) + *rkey = NULL; + return NULL; + } + + hash = bucket_hash(h, key); + e = hash_scan(h, hash, key); + if (!e) { + if (rkey) + *rkey = NULL; + return NULL; + } + + data = e->value; + if (rkey) + *rkey = (void*) e->key; + + remove_entry(h, e); + + return data; +} + int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { struct hashmap_entry *e; unsigned old_hash, new_hash; @@ -692,59 +761,6 @@ at_end: return NULL; } -void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) { - struct hashmap_entry *e; - - assert(i); - - if (!h) - goto at_beginning; - - if (*i == ITERATOR_FIRST) - goto at_beginning; - - if (*i == ITERATOR_LAST && !h->iterate_list_tail) - goto at_beginning; - - e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i; - - if (e->iterate_previous) - *i = (Iterator) e->iterate_previous; - else - *i = ITERATOR_FIRST; - - if (key) - *key = e->key; - - return e->value; - -at_beginning: - *i = ITERATOR_FIRST; - - if (key) - *key = NULL; - - return NULL; -} - -void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) { - unsigned hash; - struct hashmap_entry *e; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - - e = hash_scan(h, hash, key); - if (!e) - return NULL; - - *i = (Iterator) e; - - return e->value; -} - void* hashmap_first(Hashmap *h) { if (!h) @@ -767,17 +783,6 @@ void* hashmap_first_key(Hashmap *h) { return (void*) h->iterate_list_head->key; } -void* hashmap_last(Hashmap *h) { - - if (!h) - return NULL; - - if (!h->iterate_list_tail) - return NULL; - - return h->iterate_list_tail->value; -} - void* hashmap_steal_first(Hashmap *h) { void *data; @@ -881,15 +886,15 @@ int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { unsigned h_hash, other_hash; struct hashmap_entry *e; - if (!other) - return 0; - assert(h); h_hash = bucket_hash(h, key); if (hash_scan(h, h_hash, key)) return -EEXIST; + if (!other) + return -ENOENT; + other_hash = bucket_hash(other, key); e = hash_scan(other, other_hash, key); if (!e) @@ -906,7 +911,7 @@ Hashmap *hashmap_copy(Hashmap *h) { assert(h); - copy = hashmap_new(h->hash_func, h->compare_func); + copy = hashmap_new(h->hash_ops); if (!copy) return NULL; @@ -940,7 +945,6 @@ void *hashmap_next(Hashmap *h, const void *key) { unsigned hash; struct hashmap_entry *e; - assert(h); assert(key); if (!h)