summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Schmidt <mschmidt@redhat.com>2014-09-15 16:43:59 -0400
committerAnthony G. Basile <blueness@gentoo.org>2014-09-15 16:43:59 -0400
commit7398282096ba4a74b11c18082cab7db32c2b0da1 (patch)
tree77eaf24ef7c223963ab4d498d5f350932b17ff6c
parent35257b404e1b449bcfbcbbf7dd4a713927bec8ed (diff)
hashmap: introduce hash_ops to make struct Hashmap smaller
It is redundant to store 'hash' and 'compare' function pointers in struct Hashmap separately. The functions always comprise a pair. Store a single pointer to struct hash_ops instead. systemd keeps hundreds of hashmaps, so this saves a little bit of memory. Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
-rw-r--r--src/shared/cgroup-util.c2
-rw-r--r--src/shared/conf-files.c2
-rw-r--r--src/shared/hashmap.c111
-rw-r--r--src/shared/hashmap.h11
-rw-r--r--src/shared/set.c8
-rw-r--r--src/shared/set.h2
6 files changed, 69 insertions, 67 deletions
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index 76ab747292..3f9018cf1f 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -84,7 +84,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo
* tasks list, to properly handle forking processes */
if (!s) {
- s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
+ s = allocated_set = set_new(NULL);
if (!s)
return -ENOMEM;
}
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index 6675a1c26c..c68f361136 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -107,7 +107,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
if (!path_strv_resolve_uniq(dirs, root))
return -ENOMEM;
- fh = hashmap_new(string_hash_func, string_compare_func);
+ fh = hashmap_new(&string_hash_ops);
if (!fh)
return -ENOMEM;
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index 38dce11921..95ef918ed3 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -37,8 +37,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;
@@ -119,6 +118,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);
@@ -129,8 +133,13 @@ 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
+};
+
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) {
@@ -152,7 +161,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;
@@ -174,8 +183,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;
@@ -314,7 +322,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;
@@ -346,7 +354,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)
@@ -358,7 +366,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;
@@ -378,19 +386,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);
@@ -411,6 +410,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);
+}
+
void* hashmap_get(Hashmap *h, const void *key) {
unsigned hash;
struct hashmap_entry *e;
@@ -426,6 +442,24 @@ void* hashmap_get(Hashmap *h, const void *key) {
return e->value;
}
+void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
+ 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;
+
+ if (key2)
+ *key2 = (void*) e->key;
+
+ return e->value;
+}
+
bool hashmap_contains(Hashmap *h, const void *key) {
unsigned hash;
@@ -471,41 +505,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_steal_first(Hashmap *h) {
void *data;
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index b739262ab3..53b04eb033 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -41,27 +41,34 @@ typedef _IteratorStruct* Iterator;
typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
typedef int (*compare_func_t)(const void *a, const void *b);
+struct hash_ops {
+ hash_func_t hash;
+ compare_func_t compare;
+};
+
unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int string_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops string_hash_ops;
/* This will compare the passed pointers directly, and will not
* dereference them. This is hence not useful for strings or
* suchlike. */
unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int trivial_compare_func(const void *a, const void *b) _const_;
+extern const struct hash_ops trivial_hash_ops;
-Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
+Hashmap *hashmap_new(const struct hash_ops *hash_ops);
void hashmap_free(Hashmap *h);
void hashmap_free_free(Hashmap *h);
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);
unsigned hashmap_size(Hashmap *h) _pure_;
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
-void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
void hashmap_clear(Hashmap *h);
void hashmap_clear_free(Hashmap *h);
diff --git a/src/shared/set.c b/src/shared/set.c
index 8b89d45cc4..4b8c76feca 100644
--- a/src/shared/set.c
+++ b/src/shared/set.c
@@ -27,8 +27,8 @@
/* For now this is not much more than a wrapper around a hashmap */
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
- return MAKE_SET(hashmap_new(hash_func, compare_func));
+Set *set_new(const struct hash_ops *hash_ops) {
+ return MAKE_SET(hashmap_new(hash_ops));
}
void set_free(Set* s) {
@@ -50,7 +50,3 @@ bool set_contains(Set *s, void *value) {
void *set_iterate(Set *s, Iterator *i) {
return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
}
-
-void *set_iterate_backwards(Set *s, Iterator *i) {
- return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
-}
diff --git a/src/shared/set.h b/src/shared/set.h
index 39a5a495f5..b2f473428e 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -30,7 +30,7 @@
typedef struct Set Set;
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
+Set *set_new(const struct hash_ops *hash_ops);
void set_free(Set* s);
int set_put(Set *s, void *value);