X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Fhashmap.h;h=9c6e0cab186a746de3203c05e9567f9fad0f6e9c;hp=65fb3c0ee984d591c327db56f26b89eeb27046cc;hb=cc13b327297bb616f8adca10c7503581c8bc5b93;hpb=7ad63f57b6ce7ae9e3cc19dcb441f0a4494fa3f2 diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 65fb3c0ee..9c6e0cab1 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -6,6 +6,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + Copyright 2014 Michal Schmidt systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -26,20 +27,45 @@ #include "macro.h" #include "util.h" -/* Pretty straightforward hash table implementation. As a minor - * optimization a NULL hashmap object will be treated as empty hashmap - * for all read operations. That way it is not necessary to - * instantiate an object for each Hashmap use. */ +/* + * A hash table implementation. As a minor optimization a NULL hashmap object + * will be treated as empty hashmap for all read operations. That way it is not + * necessary to instantiate an object for each Hashmap use. + * + * If ENABLE_HASHMAP_DEBUG is defined (by configuring with --enable-hashmap-debug), + * the implemention will: + * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) + * - perform extra checks for invalid use of iterators + */ #define HASH_KEY_SIZE 16 -typedef struct Hashmap Hashmap; -typedef struct OrderedHashmap OrderedHashmap; -typedef struct _IteratorStruct _IteratorStruct; -typedef _IteratorStruct* Iterator; +/* The base type for all hashmap and set types. Many functions in the + * implementation take (HashmapBase*) parameters and are run-time polymorphic, + * though the API is not meant to be polymorphic (do not call functions + * prefixed with two underscores directly). */ +typedef struct HashmapBase HashmapBase; + +/* Specific hashmap/set types */ +typedef struct Hashmap Hashmap; /* Maps keys to values */ +typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ +typedef struct Set Set; /* Stores just keys */ + +/* Ideally the Iterator would be an opaque struct, but it is instantiated + * by hashmap users, so the definition has to be here. Do not use its fields + * directly. */ +typedef struct { + unsigned idx; /* index of an entry to be iterated next */ + const void *next_key; /* expected value of that entry's key pointer */ +#ifdef ENABLE_HASHMAP_DEBUG + unsigned put_count; /* hashmap's put_count recorded at start of iteration */ + unsigned rem_count; /* hashmap's rem_count in previous iteration */ + unsigned prev_idx; /* idx in previous iteration */ +#endif +} Iterator; -#define ITERATOR_FIRST ((Iterator) 0) -#define ITERATOR_LAST ((Iterator) -1) +#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) +#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) 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); @@ -82,153 +108,287 @@ extern const struct hash_ops devt_hash_ops = { #define devt_hash_ops uint64_hash_ops #endif -Hashmap *hashmap_new(const struct hash_ops *hash_ops); -static inline OrderedHashmap *ordered_hashmap_new(const struct hash_ops *hash_ops) { - return (OrderedHashmap*) hashmap_new(hash_ops); +/* Macros for type checking */ +#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ + (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ + __builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ + __builtin_types_compatible_p(typeof(h), Set*)) + +#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ + (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ + +#define HASHMAP_BASE(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ + (HashmapBase*)(h), \ + (void)0) + +#define PLAIN_HASHMAP(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ + (Hashmap*)(h), \ + (void)0) + +#ifdef ENABLE_HASHMAP_DEBUG +# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line +# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ +# define HASHMAP_DEBUG_PASS_ARGS , func, file, line +#else +# define HASHMAP_DEBUG_PARAMS +# define HASHMAP_DEBUG_SRC_ARGS +# define HASHMAP_DEBUG_PASS_ARGS +#endif + +Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) + +void internal_hashmap_free(HashmapBase *h); +static inline void hashmap_free(Hashmap *h) { + internal_hashmap_free(HASHMAP_BASE(h)); } -void hashmap_free(Hashmap *h); static inline void ordered_hashmap_free(OrderedHashmap *h) { - hashmap_free((Hashmap*) h); + internal_hashmap_free(HASHMAP_BASE(h)); +} + +void internal_hashmap_free_free(HashmapBase *h); +static inline void hashmap_free_free(Hashmap *h) { + internal_hashmap_free_free(HASHMAP_BASE(h)); } -void hashmap_free_free(Hashmap *h); static inline void ordered_hashmap_free_free(OrderedHashmap *h) { - hashmap_free_free((Hashmap*) h); + internal_hashmap_free_free(HASHMAP_BASE(h)); } + void hashmap_free_free_free(Hashmap *h); static inline void ordered_hashmap_free_free_free(OrderedHashmap *h) { - hashmap_free_free_free((Hashmap*) h); + hashmap_free_free_free(PLAIN_HASHMAP(h)); } -Hashmap *hashmap_copy(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) hashmap_copy((Hashmap*) h); + +HashmapBase *internal_hashmap_copy(HashmapBase *h); +static inline Hashmap *hashmap_copy(Hashmap *h) { + return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } -int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops); -static inline int ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops) { - return hashmap_ensure_allocated((Hashmap**) h, hash_ops); +static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { + return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } +int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) + int hashmap_put(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put((Hashmap*) h, key, value); + return hashmap_put(PLAIN_HASHMAP(h), key, value); } + 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((Hashmap*) h, key, value); + return hashmap_update(PLAIN_HASHMAP(h), key, value); } + int hashmap_replace(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace((Hashmap*) h, key, value); + return hashmap_replace(PLAIN_HASHMAP(h), key, value); +} + +void *internal_hashmap_get(HashmapBase *h, const void *key); +static inline void *hashmap_get(Hashmap *h, const void *key) { + return internal_hashmap_get(HASHMAP_BASE(h), key); } -void *hashmap_get(Hashmap *h, const void *key); static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return hashmap_get((Hashmap*) h, key); + return internal_hashmap_get(HASHMAP_BASE(h), key); } + void *hashmap_get2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2((Hashmap*) h, key, rkey); + return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); +} + +bool internal_hashmap_contains(HashmapBase *h, const void *key); +static inline bool hashmap_contains(Hashmap *h, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(h), key); } -bool hashmap_contains(Hashmap *h, const void *key); static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return hashmap_contains((Hashmap*) h, key); + return internal_hashmap_contains(HASHMAP_BASE(h), key); +} + +void *internal_hashmap_remove(HashmapBase *h, const void *key); +static inline void *hashmap_remove(Hashmap *h, const void *key) { + return internal_hashmap_remove(HASHMAP_BASE(h), key); } -void *hashmap_remove(Hashmap *h, const void *key); static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return hashmap_remove((Hashmap*) h, key); + return internal_hashmap_remove(HASHMAP_BASE(h), key); } + void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2((Hashmap*) h, key, rkey); + return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } + void *hashmap_remove_value(Hashmap *h, const void *key, void *value); static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value((Hashmap*) h, key, value); + return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); } + int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put((Hashmap*) h, old_key, new_key, value); + return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); } + int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace((Hashmap*) h, old_key, new_key, value); + return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); } -int hashmap_merge(Hashmap *h, Hashmap *other); -static inline int ordered_hashmap_merge(OrderedHashmap *h, OrderedHashmap *other) { - return hashmap_merge((Hashmap*) h, (Hashmap*) other); +/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa + * should just work, allow this by having looser type-checking here. */ +int internal_hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) + +int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int hashmap_reserve(Hashmap *h, unsigned entries_add); static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return hashmap_reserve((Hashmap*) h, entries_add); + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} + +int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ +static inline int hashmap_move(Hashmap *h, Hashmap *other) { + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int hashmap_move(Hashmap *h, Hashmap *other); static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return hashmap_move((Hashmap*) h, (Hashmap*) other); + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); +} + +int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key); static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return hashmap_move_one((Hashmap*) h, (Hashmap*) other, key); + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned hashmap_size(Hashmap *h) _pure_; +unsigned internal_hashmap_size(HashmapBase *h) _pure_; +static inline unsigned hashmap_size(Hashmap *h) { + return internal_hashmap_size(HASHMAP_BASE(h)); +} static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return hashmap_size((Hashmap*) h); + return internal_hashmap_size(HASHMAP_BASE(h)); +} + +static inline bool hashmap_isempty(Hashmap *h) { + return hashmap_size(h) == 0; } -bool hashmap_isempty(Hashmap *h) _pure_; static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return hashmap_isempty((Hashmap*) h); + return ordered_hashmap_size(h) == 0; +} + +unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +static inline unsigned hashmap_buckets(Hashmap *h) { + return internal_hashmap_buckets(HASHMAP_BASE(h)); } -unsigned hashmap_buckets(Hashmap *h) _pure_; static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return hashmap_buckets((Hashmap*) h); + return internal_hashmap_buckets(HASHMAP_BASE(h)); } -void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key); +void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key); +static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) { + return internal_hashmap_iterate(HASHMAP_BASE(h), i, key); +} static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) { - return hashmap_iterate((Hashmap*) h, i, key); + return internal_hashmap_iterate(HASHMAP_BASE(h), i, key); } -void hashmap_clear(Hashmap *h); +void internal_hashmap_clear(HashmapBase *h); +static inline void hashmap_clear(Hashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h)); +} static inline void ordered_hashmap_clear(OrderedHashmap *h) { - hashmap_clear((Hashmap*) h); + internal_hashmap_clear(HASHMAP_BASE(h)); +} + +void internal_hashmap_clear_free(HashmapBase *h); +static inline void hashmap_clear_free(Hashmap *h) { + internal_hashmap_clear_free(HASHMAP_BASE(h)); } -void hashmap_clear_free(Hashmap *h); static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - hashmap_clear_free((Hashmap*) h); + internal_hashmap_clear_free(HASHMAP_BASE(h)); } + void hashmap_clear_free_free(Hashmap *h); static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free((Hashmap*) h); + hashmap_clear_free_free(PLAIN_HASHMAP(h)); } -void *hashmap_steal_first(Hashmap *h); +/* + * Note about all *_first*() functions + * + * For plain Hashmaps and Sets the order of entries is undefined. + * The functions find whatever entry is first in the implementation + * internal order. + * + * Only for OrderedHashmaps the order is well defined and finding + * the first entry is O(1). + */ + +void *internal_hashmap_steal_first(HashmapBase *h); +static inline void *hashmap_steal_first(Hashmap *h) { + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return hashmap_steal_first((Hashmap*) h); + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} + +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)); } -void *hashmap_steal_first_key(Hashmap *h); static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return hashmap_steal_first_key((Hashmap*) h); + return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); } -void *hashmap_first(Hashmap *h) _pure_; -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return hashmap_first((Hashmap*) 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)); } -void *hashmap_first_key(Hashmap *h) _pure_; static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return hashmap_first_key((Hashmap*) h); + return internal_hashmap_first_key(HASHMAP_BASE(h)); } -void *hashmap_next(Hashmap *h, const void *key); -static inline void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - return hashmap_next((Hashmap*) h, key); +void *internal_hashmap_first(HashmapBase *h) _pure_; +static inline void *hashmap_first(Hashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); } +static inline void *ordered_hashmap_first(OrderedHashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); +} + +/* no hashmap_next */ +void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **hashmap_get_strv(Hashmap *h); +char **internal_hashmap_get_strv(HashmapBase *h); +static inline char **hashmap_get_strv(Hashmap *h) { + return internal_hashmap_get_strv(HASHMAP_BASE(h)); +} static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return hashmap_get_strv((Hashmap*) h); + return internal_hashmap_get_strv(HASHMAP_BASE(h)); } +/* + * Hashmaps are iterated in unpredictable order. + * OrderedHashmaps are an exception to this. They are iterated in the order + * the entries were inserted. + * It is safe to remove the current entry. + */ #define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL)) + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \ + (e); \ + (e) = hashmap_iterate((h), &(i), NULL)) #define ORDERED_HASHMAP_FOREACH(e, h, i) \ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \ @@ -236,7 +396,9 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { (e) = ordered_hashmap_iterate((h), &(i), NULL)) #define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k))) + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \ + (e); \ + (e) = hashmap_iterate((h), &(i), (const void**) &(k))) #define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \ @@ -249,6 +411,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); + #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) #define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) #define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)