hashmap.c revision de99c9dcbaf6e474551266d8f0b519bf2d8d0522
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba This file is part of systemd.
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba Copyright 2010 Lennart Poettering
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba systemd is free software; you can redistribute it and/or modify it
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba under the terms of the GNU Lesser General Public License as published by
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba the Free Software Foundation; either version 2.1 of the License, or
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba (at your option) any later version.
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba systemd is distributed in the hope that it will be useful, but
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba WITHOUT ANY WARRANTY; without even the implied warranty of
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba Lesser General Public License for more details.
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba You should have received a copy of the GNU Lesser General Public License
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba along with systemd; If not, see <http://www.gnu.org/licenses/>.
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba const void *key;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba struct hashmap_entry *iterate_next, *iterate_previous;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba struct hashmap_entry *iterate_list_head, *iterate_list_tail;
0d397efc4b781ef5b60108708fa1131467d2c3c8gbellatostatic void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size, unsigned at_least) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba unsigned i;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba /* When a tile is released we add it to the list and simply
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba * place the next pointer at its offset 0. */
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba unsigned n;
d87eec6ea79502a1c14f1d960527ba986dfeb52cJnRouvignac size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambastatic void deallocate_tile(void **first_tile, void *p) {
85f6e15f35fa13ce5e3d0ed1716c8986b048745emrossign * (void**) p = *first_tile;
8b15572442aae50358e392562bd2e1ebfd5b2906matthew while (p) {
8b15572442aae50358e392562bd2e1ebfd5b2906matthew__attribute__((destructor)) static void cleanup_pool(void) {
85f6e15f35fa13ce5e3d0ed1716c8986b048745emrossign /* Be nice to valgrind */
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaunsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return (unsigned long) u;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint string_compare_func(const void *a, const void *b) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return strcmp(a, b);
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaunsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return (unsigned long) u;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint trivial_compare_func(const void *a, const void *b) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaunsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key);
d87eec6ea79502a1c14f1d960527ba986dfeb52cJnRouvignac return (unsigned long) u;
6031e9c7eb72435516a6828deb2e97533ed0382dludovicpint uint64_compare_func(const void *_a, const void *_b) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaunsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
af0f03109bd00f4ccb3d6671bbc4e4f1a32ccbf9gbellato siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key);
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return (unsigned long) u;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint devt_compare_func(const void *_a, const void *_b) {
d729d88914866aa41d5d6dc5e7f6a7fb5f601bc3matthewstatic unsigned bucket_hash(Hashmap *h, const void *p) {
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew return (unsigned) (h->hash_func(p, h->hash_key) % h->n_buckets);
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthewstatic void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
d729d88914866aa41d5d6dc5e7f6a7fb5f601bc3matthew static bool current_initialized = false;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* Returns a hash function key to use. In order to keep things
d729d88914866aa41d5d6dc5e7f6a7fb5f601bc3matthew * fast we will not generate a new key each time we allocate a
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew * new hash table. Instead, we'll just reuse the most recently
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew * generated one, except if we never generated one or when we
d729d88914866aa41d5d6dc5e7f6a7fb5f601bc3matthew * are rehashing an entire hash table because we reached a
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba * fill level */
6031e9c7eb72435516a6828deb2e97533ed0382dludovicpHashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
49ec0e527beda838ac41d558df188d2c7fa7a1c8ludo size = ALIGN(sizeof(Hashmap)) + INITIAL_N_BUCKETS * sizeof(struct hashmap_entry*);
5d335b755ccd43707ff8529f93345030d559bc81matthew h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size, 8);
8b15572442aae50358e392562bd2e1ebfd5b2906matthew h->hash_func = hash_func ? hash_func : trivial_hash_func;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba h->compare_func = compare_func ? compare_func : trivial_compare_func;
49ec0e527beda838ac41d558df188d2c7fa7a1c8ludo h->buckets = (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)));
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthewint hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambastatic void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
49ec0e527beda838ac41d558df188d2c7fa7a1c8ludo /* Insert into hash table */
afd6ce83f9ecfa7b375c1f72eb5f279bbd01568cmatthew /* Insert into iteration list */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthewstatic void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* Remove from iteration list */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew e->iterate_next->iterate_previous = e->iterate_previous;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew e->iterate_previous->iterate_next = e->iterate_next;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba /* Remove from hash table bucket list */
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba e->bucket_next->bucket_previous = e->bucket_previous;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambastatic void remove_entry(Hashmap *h, struct hashmap_entry *e) {
6031e9c7eb72435516a6828deb2e97533ed0382dludovicp /* Free the hashmap, but nothing in it */
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
8b15572442aae50358e392562bd2e1ebfd5b2906matthew /* Free the hashmap and all data objects in it, but not the
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* Free the hashmap and all data and key objects in it */
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba while ((p = hashmap_steal_first(h)))
49ec0e527beda838ac41d558df188d2c7fa7a1c8ludo void *a, *b;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambastatic struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
afd6ce83f9ecfa7b375c1f72eb5f279bbd01568cmatthew struct hashmap_entry **n, *i;
afd6ce83f9ecfa7b375c1f72eb5f279bbd01568cmatthew unsigned m;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew return false;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* Increase by four */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* If we hit OOM we simply risk packed hashmaps... */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew return false;
85f6e15f35fa13ce5e3d0ed1716c8986b048745emrossign /* Let's use a different randomized hash key for the
85f6e15f35fa13ce5e3d0ed1716c8986b048745emrossign * extension, so that people cannot guess what we are using
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew * here forever */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew for (i = h->iterate_list_head; i; i = i->iterate_next) {
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew old_bucket = h->hash_func(i->key, h->hash_key) % h->n_buckets;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew /* First, drop from old bucket table */
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthew i->bucket_next->bucket_previous = i->bucket_previous;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba /* Then, add to new backet table */
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba return true;
f38f1b1dd4d4521b3ab1160332de4f1767e6ddfamatthewint hashmap_put(Hashmap *h, const void *key, void *value) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry), 64U);
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint hashmap_replace(Hashmap *h, const void *key, void *value) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint hashmap_update(Hashmap *h, const void *key, void *value) {
49ec0e527beda838ac41d558df188d2c7fa7a1c8ludovoid* hashmap_get2(Hashmap *h, const void *key, void **key2) {
0b5792318cb29b979a4811a1aa237d35e3919830JnRouvignacbool hashmap_contains(Hashmap *h, const void *key) {
0b5792318cb29b979a4811a1aa237d35e3919830JnRouvignac return false;
0b5792318cb29b979a4811a1aa237d35e3919830JnRouvignacvoid* hashmap_remove(Hashmap *h, const void *key) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambavoid* hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambaint hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) {
bbbf74f3a03a1063865820855dfafd2dc368ce0cpgambaint hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba struct hashmap_entry *e, *k;
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba if (e != k)
91c1c33da74bf7248cbf46057d5721f53fef2cf4matthewvoid* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgambavoid *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
eef6695bc3b88e10d60477095ebddd4b9499481fpgambavoid *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
eef6695bc3b88e10d60477095ebddd4b9499481fpgamba e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
df880e8f097ecf074c379e7137f2672437ac858fmatthewvoid *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
return key;
return h->n_entries;
return h->n_buckets;
return h->n_entries == 0;
struct hashmap_entry *e;
assert(h);
if (!other)
if (r < 0 && r != -EEXIST)
struct hashmap_entry *e, *n;
assert(h);
if (!other)
n = e->iterate_next;
struct hashmap_entry *e;
if (!other)
assert(h);
return -EEXIST;
return -ENOENT;
assert(h);
if (!copy)
return NULL;
return NULL;
return copy;
char **sv;
char *item;
if (!sv)
return NULL;
return sv;
unsigned hash;
struct hashmap_entry *e;
assert(h);
return NULL;
return NULL;
e = e->iterate_next;
return NULL;
return e->value;