da2bf9
From 3bee193141bdf3106732a2c925ffaf5ce48f0ecb Mon Sep 17 00:00:00 2001
da2bf9
From: Yu Watanabe <watanabe.yu+github@gmail.com>
da2bf9
Date: Tue, 27 Nov 2018 22:25:40 +0900
da2bf9
Subject: [PATCH] hash-func: add destructors for key and value
da2bf9
da2bf9
If they are set, then they are called in hashmap_clear() or
da2bf9
hashmap_free().
da2bf9
da2bf9
(cherry picked from commit 59a5cda7b904cd7ef9853bda15b498bbc0577524)
da2bf9
da2bf9
Resolves: #2037807
da2bf9
---
da2bf9
 src/basic/hash-funcs.h  | 54 ++++++++++++++++++++++++++---
da2bf9
 src/basic/hashmap.c     | 76 +++++++++++------------------------------
da2bf9
 src/basic/hashmap.h     | 50 ++++++++++++++++++---------
da2bf9
 src/basic/ordered-set.h |  6 ++--
da2bf9
 src/basic/set.h         | 10 +++---
da2bf9
 5 files changed, 109 insertions(+), 87 deletions(-)
da2bf9
da2bf9
diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h
da2bf9
index 2ff687e5f9..2d3125d0f9 100644
da2bf9
--- a/src/basic/hash-funcs.h
da2bf9
+++ b/src/basic/hash-funcs.h
da2bf9
@@ -1,7 +1,7 @@
da2bf9
 /* SPDX-License-Identifier: LGPL-2.1+ */
da2bf9
 #pragma once
da2bf9
 
da2bf9
-
da2bf9
+#include "alloc-util.h"
da2bf9
 #include "macro.h"
da2bf9
 #include "siphash24.h"
da2bf9
 
da2bf9
@@ -11,21 +11,67 @@ typedef int (*compare_func_t)(const void *a, const void *b);
da2bf9
 struct hash_ops {
da2bf9
         hash_func_t hash;
da2bf9
         compare_func_t compare;
da2bf9
+        free_func_t free_key;
da2bf9
+        free_func_t free_value;
da2bf9
 };
da2bf9
 
da2bf9
-#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, scope) \
da2bf9
+#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \
da2bf9
         _unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \
da2bf9
         _unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \
da2bf9
         scope const struct hash_ops name = {                            \
da2bf9
                 .hash = (hash_func_t) hash_func,                        \
da2bf9
                 .compare = (compare_func_t) compare_func,               \
da2bf9
+                .free_key = free_key_func,                              \
da2bf9
+                .free_value = free_value_func,                          \
da2bf9
+        }
da2bf9
+
da2bf9
+#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func)                 \
da2bf9
+        /* Type-safe free function */                                   \
da2bf9
+        static void UNIQ_T(wrapper_name, uq)(void *a) {                 \
da2bf9
+                type *_a = a;                                           \
da2bf9
+                func(_a);                                               \
da2bf9
         }
da2bf9
 
da2bf9
+#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \
da2bf9
+        _DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func);    \
da2bf9
+        _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func,       \
da2bf9
+                         UNIQ_T(static_free_wrapper, uq), NULL, scope)
da2bf9
+
da2bf9
+#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \
da2bf9
+        _DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \
da2bf9
+        _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func,       \
da2bf9
+                         NULL, UNIQ_T(static_free_wrapper, uq), scope)
da2bf9
+
da2bf9
+#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \
da2bf9
+        _DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \
da2bf9
+        _DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \
da2bf9
+        _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func,       \
da2bf9
+                         UNIQ_T(static_free_key_wrapper, uq),           \
da2bf9
+                         UNIQ_T(static_free_value_wrapper, uq), scope)
da2bf9
+
da2bf9
 #define DEFINE_HASH_OPS(name, type, hash_func, compare_func)            \
da2bf9
-        _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func,)
da2bf9
+        _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,)
da2bf9
 
da2bf9
 #define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func)    \
da2bf9
-        _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, static)
da2bf9
+        _DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static)
da2bf9
+
da2bf9
+#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
da2bf9
+        _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,)
da2bf9
+
da2bf9
+#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
da2bf9
+        _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static)
da2bf9
+
da2bf9
+#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
da2bf9
+        _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,)
da2bf9
+
da2bf9
+#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
da2bf9
+        _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static)
da2bf9
+
da2bf9
+#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
da2bf9
+        _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,)
da2bf9
+
da2bf9
+#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
da2bf9
+        _DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static)
da2bf9
 
da2bf9
 void string_hash_func(const void *p, struct siphash *state);
da2bf9
 int string_compare_func(const void *a, const void *b) _pure_;
da2bf9
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
da2bf9
index 69a7d70b04..7c508086f0 100644
da2bf9
--- a/src/basic/hashmap.c
da2bf9
+++ b/src/basic/hashmap.c
da2bf9
@@ -863,47 +863,38 @@ static void hashmap_free_no_clear(HashmapBase *h) {
da2bf9
                 free(h);
da2bf9
 }
da2bf9
 
da2bf9
-HashmapBase *internal_hashmap_free(HashmapBase *h) {
da2bf9
-
da2bf9
-        /* Free the hashmap, but nothing in it */
da2bf9
-
da2bf9
+HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
da2bf9
         if (h) {
da2bf9
-                internal_hashmap_clear(h);
da2bf9
+                internal_hashmap_clear(h, default_free_key, default_free_value);
da2bf9
                 hashmap_free_no_clear(h);
da2bf9
         }
da2bf9
 
da2bf9
         return NULL;
da2bf9
 }
da2bf9
 
da2bf9
-HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
da2bf9
+void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
da2bf9
+        free_func_t free_key, free_value;
da2bf9
+        if (!h)
da2bf9
+                return;
da2bf9
 
da2bf9
-        /* Free the hashmap and all data objects in it, but not the
da2bf9
-         * keys */
da2bf9
+        free_key = h->hash_ops->free_key ?: default_free_key;
da2bf9
+        free_value = h->hash_ops->free_value ?: default_free_value;
da2bf9
 
da2bf9
-        if (h) {
da2bf9
-                internal_hashmap_clear_free(h);
da2bf9
-                hashmap_free_no_clear(h);
da2bf9
-        }
da2bf9
-
da2bf9
-        return NULL;
da2bf9
-}
da2bf9
+        if (free_key || free_value) {
da2bf9
+                unsigned idx;
da2bf9
 
da2bf9
-Hashmap *hashmap_free_free_free(Hashmap *h) {
da2bf9
+                for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
da2bf9
+                     idx = skip_free_buckets(h, idx + 1)) {
da2bf9
+                        struct hashmap_base_entry *e = bucket_at(h, idx);
da2bf9
 
da2bf9
-        /* Free the hashmap and all data and key objects in it */
da2bf9
+                        if (free_key)
da2bf9
+                                free_key((void *) e->key);
da2bf9
 
da2bf9
-        if (h) {
da2bf9
-                hashmap_clear_free_free(h);
da2bf9
-                hashmap_free_no_clear(HASHMAP_BASE(h));
da2bf9
+                        if (free_value)
da2bf9
+                                free_value(entry_value(h, e));
da2bf9
+                }
da2bf9
         }
da2bf9
 
da2bf9
-        return NULL;
da2bf9
-}
da2bf9
-
da2bf9
-void internal_hashmap_clear(HashmapBase *h) {
da2bf9
-        if (!h)
da2bf9
-                return;
da2bf9
-
da2bf9
         if (h->has_indirect) {
da2bf9
                 free(h->indirect.storage);
da2bf9
                 h->has_indirect = false;
da2bf9
@@ -920,35 +911,6 @@ void internal_hashmap_clear(HashmapBase *h) {
da2bf9
         base_set_dirty(h);
da2bf9
 }
da2bf9
 
da2bf9
-void internal_hashmap_clear_free(HashmapBase *h) {
da2bf9
-        unsigned idx;
da2bf9
-
da2bf9
-        if (!h)
da2bf9
-                return;
da2bf9
-
da2bf9
-        for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
da2bf9
-             idx = skip_free_buckets(h, idx + 1))
da2bf9
-                free(entry_value(h, bucket_at(h, idx)));
da2bf9
-
da2bf9
-        internal_hashmap_clear(h);
da2bf9
-}
da2bf9
-
da2bf9
-void hashmap_clear_free_free(Hashmap *h) {
da2bf9
-        unsigned idx;
da2bf9
-
da2bf9
-        if (!h)
da2bf9
-                return;
da2bf9
-
da2bf9
-        for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
da2bf9
-             idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
da2bf9
-                struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
da2bf9
-                free((void*)e->b.key);
da2bf9
-                free(e->value);
da2bf9
-        }
da2bf9
-
da2bf9
-        internal_hashmap_clear(HASHMAP_BASE(h));
da2bf9
-}
da2bf9
-
da2bf9
 static int resize_buckets(HashmapBase *h, unsigned entries_add);
da2bf9
 
da2bf9
 /*
da2bf9
@@ -1771,7 +1733,7 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
da2bf9
         }
da2bf9
 
da2bf9
         if (r < 0) {
da2bf9
-                internal_hashmap_free(copy);
da2bf9
+                internal_hashmap_free(copy, false, false);
da2bf9
                 return NULL;
da2bf9
         }
da2bf9
 
da2bf9
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
da2bf9
index 5c70c102d7..9e4772b497 100644
da2bf9
--- a/src/basic/hashmap.h
da2bf9
+++ b/src/basic/hashmap.h
da2bf9
@@ -23,6 +23,8 @@
da2bf9
 
da2bf9
 #define HASH_KEY_SIZE 16
da2bf9
 
da2bf9
+typedef void* (*hashmap_destroy_t)(void *p);
da2bf9
+
da2bf9
 /* The base type for all hashmap and set types. Many functions in the
da2bf9
  * implementation take (HashmapBase*) parameters and are run-time polymorphic,
da2bf9
  * though the API is not meant to be polymorphic (do not call functions
da2bf9
@@ -88,25 +90,33 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HA
da2bf9
 #define hashmap_new(ops) internal_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
da2bf9
 #define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
da2bf9
 
da2bf9
-HashmapBase *internal_hashmap_free(HashmapBase *h);
da2bf9
+HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
da2bf9
 static inline Hashmap *hashmap_free(Hashmap *h) {
da2bf9
-        return (void*)internal_hashmap_free(HASHMAP_BASE(h));
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
da2bf9
 }
da2bf9
 static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
da2bf9
-        return (void*)internal_hashmap_free(HASHMAP_BASE(h));
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
da2bf9
 }
da2bf9
 
da2bf9
-HashmapBase *internal_hashmap_free_free(HashmapBase *h);
da2bf9
 static inline Hashmap *hashmap_free_free(Hashmap *h) {
da2bf9
-        return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
da2bf9
 }
da2bf9
 static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
da2bf9
-        return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
da2bf9
 }
da2bf9
 
da2bf9
-Hashmap *hashmap_free_free_free(Hashmap *h);
da2bf9
+static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
da2bf9
+}
da2bf9
+static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
da2bf9
+}
da2bf9
+
da2bf9
+static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
da2bf9
+}
da2bf9
 static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
da2bf9
-        return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
da2bf9
+        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
da2bf9
 }
da2bf9
 
da2bf9
 IteratedCache *iterated_cache_free(IteratedCache *cache);
da2bf9
@@ -259,25 +269,33 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
da2bf9
         return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
da2bf9
 }
da2bf9
 
da2bf9
-void internal_hashmap_clear(HashmapBase *h);
da2bf9
+void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
da2bf9
 static inline void hashmap_clear(Hashmap *h) {
da2bf9
-        internal_hashmap_clear(HASHMAP_BASE(h));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
da2bf9
 }
da2bf9
 static inline void ordered_hashmap_clear(OrderedHashmap *h) {
da2bf9
-        internal_hashmap_clear(HASHMAP_BASE(h));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
da2bf9
 }
da2bf9
 
da2bf9
-void internal_hashmap_clear_free(HashmapBase *h);
da2bf9
 static inline void hashmap_clear_free(Hashmap *h) {
da2bf9
-        internal_hashmap_clear_free(HASHMAP_BASE(h));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
da2bf9
 }
da2bf9
 static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
da2bf9
-        internal_hashmap_clear_free(HASHMAP_BASE(h));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
da2bf9
 }
da2bf9
 
da2bf9
-void hashmap_clear_free_free(Hashmap *h);
da2bf9
+static inline void hashmap_clear_free_key(Hashmap *h) {
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
da2bf9
+}
da2bf9
+static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
da2bf9
+}
da2bf9
+
da2bf9
+static inline void hashmap_clear_free_free(Hashmap *h) {
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), free, free);
da2bf9
+}
da2bf9
 static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
da2bf9
-        hashmap_clear_free_free(PLAIN_HASHMAP(h));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(h), free, free);
da2bf9
 }
da2bf9
 
da2bf9
 /*
da2bf9
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
da2bf9
index e7c054d8e4..7cbb71819b 100644
da2bf9
--- a/src/basic/ordered-set.h
da2bf9
+++ b/src/basic/ordered-set.h
da2bf9
@@ -21,13 +21,11 @@ static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash
da2bf9
 }
da2bf9
 
da2bf9
 static inline OrderedSet* ordered_set_free(OrderedSet *s) {
da2bf9
-        ordered_hashmap_free((OrderedHashmap*) s);
da2bf9
-        return NULL;
da2bf9
+        return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
da2bf9
 }
da2bf9
 
da2bf9
 static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
da2bf9
-        ordered_hashmap_free_free((OrderedHashmap*) s);
da2bf9
-        return NULL;
da2bf9
+        return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
da2bf9
 }
da2bf9
 
da2bf9
 static inline int ordered_set_put(OrderedSet *s, void *p) {
da2bf9
diff --git a/src/basic/set.h b/src/basic/set.h
da2bf9
index 664713810d..8e12670a6e 100644
da2bf9
--- a/src/basic/set.h
da2bf9
+++ b/src/basic/set.h
da2bf9
@@ -9,13 +9,11 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
da2bf9
 #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
da2bf9
 
da2bf9
 static inline Set *set_free(Set *s) {
da2bf9
-        internal_hashmap_free(HASHMAP_BASE(s));
da2bf9
-        return NULL;
da2bf9
+        return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
da2bf9
 }
da2bf9
 
da2bf9
 static inline Set *set_free_free(Set *s) {
da2bf9
-        internal_hashmap_free_free(HASHMAP_BASE(s));
da2bf9
-        return NULL;
da2bf9
+        return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
da2bf9
 }
da2bf9
 
da2bf9
 /* no set_free_free_free */
da2bf9
@@ -76,11 +74,11 @@ static inline unsigned set_buckets(Set *s) {
da2bf9
 bool set_iterate(Set *s, Iterator *i, void **value);
da2bf9
 
da2bf9
 static inline void set_clear(Set *s) {
da2bf9
-        internal_hashmap_clear(HASHMAP_BASE(s));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
da2bf9
 }
da2bf9
 
da2bf9
 static inline void set_clear_free(Set *s) {
da2bf9
-        internal_hashmap_clear_free(HASHMAP_BASE(s));
da2bf9
+        internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
da2bf9
 }
da2bf9
 
da2bf9
 /* no set_clear_free_free */