]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hashmap: introduce hashmap_dump_sorted() and friends
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 13 Jul 2023 05:43:45 +0000 (14:43 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 13 Jul 2023 08:06:47 +0000 (17:06 +0900)
src/basic/hashmap.c
src/basic/hashmap.h
src/test/test-hashmap-plain.c

index 3093156748035ff22815b78d20c0596d1e7bbba5..ac1c73297885d9e00b15df431634632c6e87c7a7 100644 (file)
@@ -21,6 +21,7 @@
 #include "random-util.h"
 #include "set.h"
 #include "siphash24.h"
+#include "sort-util.h"
 #include "string-util.h"
 #include "strv.h"
 
@@ -2106,3 +2107,51 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
 
         return set_fnmatch_one(include_patterns, needle);
 }
+
+static int hashmap_entry_compare(
+                struct hashmap_base_entry * const *a,
+                struct hashmap_base_entry * const *b,
+                compare_func_t compare) {
+
+        assert(a && *a);
+        assert(b && *b);
+        assert(compare);
+
+        return compare((*a)->key, (*b)->key);
+}
+
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
+        _cleanup_free_ struct hashmap_base_entry **entries = NULL;
+        Iterator iter;
+        unsigned idx;
+        size_t n = 0;
+
+        assert(ret);
+
+        if (_hashmap_size(h) == 0) {
+                *ret = NULL;
+                if (ret_n)
+                        *ret_n = 0;
+                return 0;
+        }
+
+        entries = new(struct hashmap_base_entry*, _hashmap_size(h));
+        if (!entries)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH_IDX(idx, h, iter)
+                entries[n++] = bucket_at(h, idx);
+
+        assert(n == _hashmap_size(h));
+
+        typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
+
+        /* Reuse the array. */
+        FOREACH_ARRAY(e, entries, n)
+                *e = entry_value(h, *e);
+
+        *ret = (void**) TAKE_PTR(entries);
+        if (ret_n)
+                *ret_n = n;
+        return 0;
+}
index 68d9b81cf20e82f82fabac1e2c3aa7fa6dc18335..ad85093d69a8bbe810daae8eee207f6c9d2e14ed 100644 (file)
@@ -398,6 +398,17 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
         return _hashmap_get_strv(HASHMAP_BASE(h));
 }
 
+int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
+static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
+        return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
+}
+
 /*
  * Hashmaps are iterated in unpredictable order.
  * OrderedHashmaps are an exception to this. They are iterated in the order
index 560a64b959f7b777e2fbb40a64dd327ddac76121..a821d412b7eb08e16adbeb97afd7c9ba7b6f057d 100644 (file)
@@ -963,6 +963,44 @@ TEST(string_strv_hashmap) {
         assert_se(strv_equal(s, STRV_MAKE("bar", "BAR")));
 }
 
+TEST(hashmap_dump_sorted) {
+        static void * const expected[] = { UINT_TO_PTR(123U), UINT_TO_PTR(12U), UINT_TO_PTR(345U), };
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        _cleanup_free_ void **vals = NULL;
+        size_t n;
+
+        assert_se(m = hashmap_new(&string_hash_ops));
+
+        assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0);
+        assert_se(n == 0);
+        assert_se(!vals);
+
+        assert_se(hashmap_put(m, "key 0", expected[0]) == 1);
+        assert_se(hashmap_put(m, "key 1", expected[1]) == 1);
+        assert_se(hashmap_put(m, "key 2", expected[2]) == 1);
+
+        assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0);
+        assert_se(n == ELEMENTSOF(expected));
+        assert_se(memcmp(vals, expected, n * sizeof(void*)) == 0);
+
+        vals = mfree(vals);
+        m = hashmap_free(m);
+
+        assert_se(m = hashmap_new(NULL));
+
+        assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0);
+        assert_se(n == 0);
+        assert_se(!vals);
+
+        assert_se(hashmap_put(m, UINT_TO_PTR(333U), expected[2]) == 1);
+        assert_se(hashmap_put(m, UINT_TO_PTR(222U), expected[1]) == 1);
+        assert_se(hashmap_put(m, UINT_TO_PTR(111U), expected[0]) == 1);
+
+        assert_se(hashmap_dump_sorted(m, &vals, &n) >= 0);
+        assert_se(n == ELEMENTSOF(expected));
+        assert_se(memcmp(vals, expected, n * sizeof(void*)) == 0);
+}
+
 /* Signal to test-hashmap.c that tests from this compilation unit were run. */
 extern int n_extern_tests_run;
 TEST(ensure_extern_hashmap_tests) {