From: Zbigniew Jędrzejewski-Szmek Date: Thu, 11 Apr 2019 16:08:57 +0000 (+0200) Subject: basic/hashmap: add hashops variant that does strdup/freeing on its own X-Git-Tag: v243-rc1~77^2~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=87da87846dbfb07358d01f9d53f1c35ec61b447d;p=thirdparty%2Fsystemd.git basic/hashmap: add hashops variant that does strdup/freeing on its own So far, we'd use hashmap_free_free to free both keys and values along with the hashmap. I think it's better to make this more encapsulated: in this variant the way contents are freed can be decided when the hashmap is created, and users of the hashmap can always use hashmap_free. --- diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index 1be43d41a93..11cd371fad4 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -10,6 +10,9 @@ void string_hash_func(const char *p, struct siphash *state) { } DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); +DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, + char, string_hash_func, string_compare_func, free, + char, free); void path_hash_func(const char *q, struct siphash *state) { size_t n; diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h index 3d2ae4b55ee..0d2d4283893 100644 --- a/src/basic/hash-funcs.h +++ b/src/basic/hash-funcs.h @@ -76,6 +76,7 @@ struct hash_ops { void string_hash_func(const char *p, struct siphash *state); #define string_compare_func strcmp extern const struct hash_ops string_hash_ops; +extern const struct hash_ops string_hash_ops_free_free; void path_hash_func(const char *p, struct siphash *state); int path_compare_func(const char *a, const char *b) _pure_; diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index f244d767da7..c33e00fc598 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1768,6 +1768,32 @@ int set_consume(Set *s, void *value) { return r; } +int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) { + int r; + + r = hashmap_ensure_allocated(h, &string_hash_ops_free_free); + if (r < 0) + return r; + + _cleanup_free_ char *kdup = NULL, *vdup = NULL; + kdup = strdup(k); + vdup = strdup(v); + if (!kdup || !vdup) + return -ENOMEM; + + r = hashmap_put(*h, kdup, vdup); + if (r < 0) { + if (r == -EEXIST && streq(v, hashmap_get(*h, kdup))) + return 0; + return r; + } + + assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */ + kdup = vdup = NULL; + + return 0; +} + int set_put_strdup(Set *s, const char *p) { char *c; diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index 1b071c230e7..65adc92513d 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -147,6 +147,8 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void * return hashmap_put(PLAIN_HASHMAP(h), key, value); } +int hashmap_put_strdup(Hashmap **h, const char *k, const char *v); + int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { return hashmap_update(PLAIN_HASHMAP(h), key, value); diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index fdb0c7e87ff..02cc396cf75 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -978,6 +978,33 @@ static void test_hashmap_reserve(void) { assert_se(hashmap_reserve(m, UINT_MAX - 1) == -ENOMEM); } +static void test_string_strv_hashmap(void) { + _cleanup_hashmap_free_ Hashmap *m = NULL; + char **s; + + log_info("/* %s */", __func__); + + assert_se(string_strv_hashmap_put(&m, "foo", "bar") == 1); + assert_se(string_strv_hashmap_put(&m, "foo", "bar") == 0); + assert_se(string_strv_hashmap_put(&m, "foo", "BAR") == 1); + assert_se(string_strv_hashmap_put(&m, "foo", "BAR") == 0); + assert_se(string_strv_hashmap_put(&m, "foo", "bar") == 0); + assert_se(hashmap_contains(m, "foo")); + + s = hashmap_get(m, "foo"); + assert_se(strv_equal(s, STRV_MAKE("bar", "BAR"))); + + assert_se(string_strv_hashmap_put(&m, "xxx", "bar") == 1); + assert_se(string_strv_hashmap_put(&m, "xxx", "bar") == 0); + assert_se(string_strv_hashmap_put(&m, "xxx", "BAR") == 1); + assert_se(string_strv_hashmap_put(&m, "xxx", "BAR") == 0); + assert_se(string_strv_hashmap_put(&m, "xxx", "bar") == 0); + assert_se(hashmap_contains(m, "xxx")); + + s = hashmap_get(m, "xxx"); + assert_se(strv_equal(s, STRV_MAKE("bar", "BAR"))); +} + void test_hashmap_funcs(void) { log_parse_environment(); log_open(); @@ -1012,4 +1039,5 @@ void test_hashmap_funcs(void) { test_hashmap_clear_free_free(); test_hashmap_clear_free_with_destructor(); test_hashmap_reserve(); + test_string_strv_hashmap(); }