diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 018ef5e7d8..eba56add1f 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1543,47 +1543,9 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; + void *key, *data; unsigned idx; idx = find_first_entry(h); @@ -1592,9 +1554,15 @@ void *internal_hashmap_steal_first_key(HashmapBase *h) { e = bucket_at(h, idx); key = (void*) e->key; - remove_entry(h, idx); + data = entry_value(h, e); - return key; + if (remove) + remove_entry(h, idx); + + if (ret_key) + *ret_key = key; + + return data; } unsigned internal_hashmap_size(HashmapBase *h) { diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index b771ceccdc..bb2a5c76ec 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -290,36 +290,51 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_steal_first(HashmapBase *h); +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} +static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} + + static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } - -void *internal_hashmap_steal_first_key(HashmapBase *h); -static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; -static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); +} + +static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { + void *key = NULL; + + (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + return key; +} +static inline void *hashmap_steal_first_key(Hashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h), true); +} +static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h), true); +} +static inline void *hashmap_first_key(Hashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h), false); +} +static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ diff --git a/src/basic/set.h b/src/basic/set.h index 664713810d..0d99d5550d 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -86,7 +86,7 @@ static inline void set_clear_free(Set *s) { /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -105,7 +105,7 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL); } /* no set_next */ diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index c7e5bf4431..3ae283e93c 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -792,6 +792,33 @@ static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { return r; } +static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) { + sd_device **a, **b, **end; + + assert(enumerator); + + if (enumerator->n_devices <= 1) + return; + + a = enumerator->devices + 1; + b = enumerator->devices; + end = enumerator->devices + enumerator->n_devices; + + for (; a < end; a++) { + const char *devpath_a, *devpath_b; + + assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0); + + if (path_equal(devpath_a, devpath_b)) + sd_device_unref(*a); + else + *(++b) = *a; + } + + enumerator->n_devices = b - enumerator->devices + 1; +} + int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { int r = 0, k; size_t i; @@ -822,6 +849,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { } typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); + device_enumerator_dedup_devices(enumerator); enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; @@ -906,6 +934,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { } typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); + device_enumerator_dedup_devices(enumerator); enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index fc2d02b4b3..e2701e0e24 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -3,6 +3,7 @@ #include "device-enumerator-private.h" #include "device-private.h" #include "device-util.h" +#include "hashmap.h" #include "string-util.h" #include "tests.h" #include "util.h" @@ -63,9 +64,73 @@ static void test_sd_device_basic(void) { } } +static void test_sd_device_enumerator_filter_subsystem_one(const char *subsystem, Hashmap *h) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d, *t; + + assert_se(sd_device_enumerator_new(&e) >= 0); + assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, true) >= 0); + + FOREACH_DEVICE(e, d) { + const char *syspath; + + assert_se(sd_device_get_syspath(d, &syspath) >= 0); + assert_se(t = hashmap_remove(h, syspath)); + assert_se(!sd_device_unref(t)); + + log_debug("Removed subsystem:%s syspath:%s", subsystem, syspath); + } + + assert_se(hashmap_isempty(h)); +} + +static void test_sd_device_enumerator_filter_subsystem(void) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(hashmap_freep) Hashmap *subsystems; + sd_device *d; + Hashmap *h; + char *s; + + assert_se(subsystems = hashmap_new(&string_hash_ops)); + assert_se(sd_device_enumerator_new(&e) >= 0); + + FOREACH_DEVICE(e, d) { + const char *syspath, *subsystem; + int r; + + assert_se(sd_device_get_syspath(d, &syspath) >= 0); + + r = sd_device_get_subsystem(d, &subsystem); + assert_se(r >= 0 || r == -ENOENT); + if (r < 0) + continue; + + h = hashmap_get(subsystems, subsystem); + if (!h) { + char *str; + assert_se(str = strdup(subsystem)); + assert_se(h = hashmap_new(&string_hash_ops)); + assert_se(hashmap_put(subsystems, str, h) >= 0); + } + + assert_se(hashmap_put(h, syspath, d) >= 0); + assert_se(sd_device_ref(d)); + + log_debug("Added subsystem:%s syspath:%s", subsystem, syspath); + } + + while ((h = hashmap_steal_first_key_and_value(subsystems, (void**) &s))) { + test_sd_device_enumerator_filter_subsystem_one(s, h); + hashmap_free(h); + free(s); + } +} + int main(int argc, char **argv) { test_setup_logging(LOG_INFO); test_sd_device_basic(); + test_sd_device_enumerator_filter_subsystem(); + return 0; }