hashmap.c revision 7ad63f57b6ce7ae9e3cc19dcb441f0a4494fa3f2
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "util.h"
#include "hashmap.h"
#include "macro.h"
#include "siphash24.h"
#include "mempool.h"
#define INITIAL_N_BUCKETS 31
struct hashmap_entry {
const void *key;
void *value;
};
struct Hashmap {
struct hashmap_entry ** buckets;
bool from_pool:1;
};
struct hashmap_tile {
Hashmap h;
};
#ifdef VALGRIND
/* Be nice to valgrind */
}
#endif
uint64_t u;
return (unsigned long) u;
}
int string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
const struct hash_ops string_hash_ops = {
.hash = string_hash_func,
};
uint64_t u;
return (unsigned long) u;
}
int trivial_compare_func(const void *a, const void *b) {
return a < b ? -1 : (a > b ? 1 : 0);
}
const struct hash_ops trivial_hash_ops = {
};
uint64_t u;
return (unsigned long) u;
}
uint64_t a, b;
return a < b ? -1 : (a > b ? 1 : 0);
}
const struct hash_ops uint64_hash_ops = {
.hash = uint64_hash_func,
};
#if SIZEOF_DEV_T != 8
uint64_t u;
return (unsigned long) u;
}
dev_t a, b;
return a < b ? -1 : (a > b ? 1 : 0);
}
const struct hash_ops devt_hash_ops = {
.hash = devt_hash_func,
};
#endif
static unsigned bucket_hash(Hashmap *h, const void *p) {
}
static bool current_initialized = false;
/* Returns a hash function key to use. In order to keep things
* fast we will not generate a new key each time we allocate a
* new hash table. Instead, we'll just reuse the most recently
* generated one, except if we never generated one or when we
* are rehashing an entire hash table because we reached a
* fill level */
if (!current_initialized || !reuse_is_ok) {
current_initialized = true;
}
}
bool b;
struct hashmap_tile *ht;
Hashmap *h;
b = is_main_thread();
if (b) {
if (!ht)
return NULL;
} else {
if (!ht)
return NULL;
}
h = &ht->h;
h->n_buckets = INITIAL_N_BUCKETS;
h->n_entries = 0;
h->from_pool = b;
get_hash_key(h->hash_key, true);
return h;
}
Hashmap *q;
assert(h);
if (*h)
return 0;
q = hashmap_new(hash_ops);
if (!q)
return -ENOMEM;
*h = q;
return 0;
}
assert(h);
assert(e);
/* Insert into hash table */
e->bucket_previous = NULL;
/* Insert into iteration list */
e->iterate_previous = h->iterate_list_tail;
e->iterate_next = NULL;
if (h->iterate_list_tail) {
assert(h->iterate_list_head);
h->iterate_list_tail->iterate_next = e;
} else {
assert(!h->iterate_list_head);
h->iterate_list_head = e;
}
h->iterate_list_tail = e;
h->n_entries++;
}
assert(h);
assert(e);
/* Remove from iteration list */
if (e->iterate_next)
else
h->iterate_list_tail = e->iterate_previous;
if (e->iterate_previous)
else
h->iterate_list_head = e->iterate_next;
/* Remove from hash table bucket list */
if (e->bucket_next)
if (e->bucket_previous)
else
h->n_entries--;
}
unsigned hash;
assert(h);
assert(e);
unlink_entry(h, e, hash);
if (h->from_pool)
else
free(e);
}
void hashmap_free(Hashmap*h) {
/* Free the hashmap, but nothing in it */
if (!h)
return;
hashmap_clear(h);
if (h->from_pool)
else
free(h);
}
void hashmap_free_free(Hashmap *h) {
/* Free the hashmap and all data objects in it, but not the
* keys */
if (!h)
return;
hashmap_free(h);
}
void hashmap_free_free_free(Hashmap *h) {
/* Free the hashmap and all data and key objects in it */
if (!h)
return;
hashmap_free(h);
}
void hashmap_clear(Hashmap *h) {
if (!h)
return;
while (h->iterate_list_head)
remove_entry(h, h->iterate_list_head);
}
void hashmap_clear_free(Hashmap *h) {
void *p;
if (!h)
return;
while ((p = hashmap_steal_first(h)))
free(p);
}
void hashmap_clear_free_free(Hashmap *h) {
if (!h)
return;
while (h->iterate_list_head) {
void *a, *b;
a = h->iterate_list_head->value;
b = (void*) h->iterate_list_head->key;
remove_entry(h, h->iterate_list_head);
free(a);
free(b);
}
}
struct hashmap_entry *e;
assert(h);
return e;
return NULL;
}
struct hashmap_entry **n, *i;
unsigned m, new_n_entries, new_n_buckets;
assert(h);
/* overflow? */
return -ENOMEM;
return 0;
/* Increase by four at least */
/* If we hit OOM we simply risk packed hashmaps... */
n = new0(struct hashmap_entry*, m);
if (!n)
return -ENOMEM;
/* Let's use a different randomized hash key for the
* extension, so that people cannot guess what we are using
* here forever */
get_hash_key(nkey, false);
for (i = h->iterate_list_head; i; i = i->iterate_next) {
unsigned long old_bucket, new_bucket;
/* First, drop from old bucket table */
if (i->bucket_next)
if (i->bucket_previous)
else
/* Then, add to new backet table */
i->bucket_next = n[new_bucket];
i->bucket_previous = NULL;
if (n[new_bucket])
n[new_bucket]->bucket_previous = i;
n[new_bucket] = i;
}
h->buckets = n;
h->n_buckets = m;
return 1;
}
/* For when we know no such entry exists yet */
struct hashmap_entry *e;
if (resize_buckets(h, 1) > 0)
if (h->from_pool)
else
if (!e)
return -ENOMEM;
link_entry(h, e, hash);
return 1;
}
struct hashmap_entry *e;
unsigned hash;
assert(h);
if (e) {
return 0;
return -EEXIST;
}
}
struct hashmap_entry *e;
unsigned hash;
assert(h);
if (e) {
return 0;
}
}
struct hashmap_entry *e;
unsigned hash;
assert(h);
if (!e)
return -ENOENT;
return 0;
}
unsigned hash;
struct hashmap_entry *e;
if (!h)
return NULL;
if (!e)
return NULL;
return e->value;
}
unsigned hash;
struct hashmap_entry *e;
if (!h)
return NULL;
if (!e)
return NULL;
if (key2)
return e->value;
}
unsigned hash;
if (!h)
return false;
}
struct hashmap_entry *e;
unsigned hash;
void *data;
if (!h)
return NULL;
if (!e)
return NULL;
remove_entry(h, e);
return data;
}
struct hashmap_entry *e;
unsigned hash;
void *data;
if (!h) {
if (rkey)
return NULL;
}
if (!e) {
if (rkey)
return NULL;
}
if (rkey)
remove_entry(h, e);
return data;
}
struct hashmap_entry *e;
if (!h)
return -ENOENT;
if (!e)
return -ENOENT;
return -EEXIST;
unlink_entry(h, e, old_hash);
link_entry(h, e, new_hash);
return 0;
}
struct hashmap_entry *e, *k;
if (!h)
return -ENOENT;
if (!e)
return -ENOENT;
if (k)
if (e != k)
remove_entry(h, k);
unlink_entry(h, e, old_hash);
link_entry(h, e, new_hash);
return 0;
}
struct hashmap_entry *e;
unsigned hash;
if (!h)
return NULL;
if (!e)
return NULL;
return NULL;
remove_entry(h, e);
return value;
}
struct hashmap_entry *e;
assert(i);
if (!h)
goto at_end;
if (*i == ITERATOR_LAST)
goto at_end;
if (*i == ITERATOR_FIRST && !h->iterate_list_head)
goto at_end;
if (e->iterate_next)
*i = (Iterator) e->iterate_next;
else
*i = ITERATOR_LAST;
if (key)
return e->value;
*i = ITERATOR_LAST;
if (key)
return NULL;
}
void* hashmap_first(Hashmap *h) {
if (!h)
return NULL;
if (!h->iterate_list_head)
return NULL;
return h->iterate_list_head->value;
}
void* hashmap_first_key(Hashmap *h) {
if (!h)
return NULL;
if (!h->iterate_list_head)
return NULL;
return (void*) h->iterate_list_head->key;
}
void* hashmap_steal_first(Hashmap *h) {
void *data;
if (!h)
return NULL;
if (!h->iterate_list_head)
return NULL;
remove_entry(h, h->iterate_list_head);
return data;
}
void* hashmap_steal_first_key(Hashmap *h) {
void *key;
if (!h)
return NULL;
if (!h->iterate_list_head)
return NULL;
remove_entry(h, h->iterate_list_head);
return key;
}
unsigned hashmap_size(Hashmap *h) {
if (!h)
return 0;
return h->n_entries;
}
unsigned hashmap_buckets(Hashmap *h) {
if (!h)
return 0;
return h->n_buckets;
}
bool hashmap_isempty(Hashmap *h) {
if (!h)
return true;
return h->n_entries == 0;
}
struct hashmap_entry *e;
assert(h);
if (!other)
return 0;
int r;
if (r < 0 && r != -EEXIST)
return r;
}
return 0;
}
int r;
assert(h);
r = resize_buckets(h, entries_add);
if (r < 0)
return r;
return 0;
}
struct hashmap_entry *e, *n;
assert(h);
/* The same as hashmap_merge(), but every new item from other
* is moved to h. */
if (!other)
return 0;
for (e = other->iterate_list_head; e; e = n) {
unsigned h_hash, other_hash;
n = e->iterate_next;
continue;
link_entry(h, e, h_hash);
}
return 0;
}
unsigned h_hash, other_hash;
struct hashmap_entry *e;
assert(h);
return -EEXIST;
if (!other)
return -ENOENT;
if (!e)
return -ENOENT;
link_entry(h, e, h_hash);
return 0;
}
assert(h);
if (!copy)
return NULL;
if (hashmap_merge(copy, h) < 0) {
return NULL;
}
return copy;
}
char **hashmap_get_strv(Hashmap *h) {
char **sv;
char *item;
int n;
if (!sv)
return NULL;
n = 0;
return sv;
}
unsigned hash;
struct hashmap_entry *e;
if (!h)
return NULL;
if (!e)
return NULL;
e = e->iterate_next;
if (!e)
return NULL;
return e->value;
}