2 This file is part of PulseAudio.
4 Copyright 2004-2008 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
26 #include <pulse/xmalloc.h>
27 #include <pulsecore/idxset.h>
28 #include <pulsecore/flist.h>
29 #include <pulsecore/macro.h>
35 struct hashmap_entry {
39 struct hashmap_entry *bucket_next, *bucket_previous;
40 struct hashmap_entry *iterate_next, *iterate_previous;
44 pa_hash_func_t hash_func;
45 pa_compare_func_t compare_func;
47 pa_free_cb_t key_free_func;
48 pa_free_cb_t value_free_func;
50 struct hashmap_entry *iterate_list_head, *iterate_list_tail;
54 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap))))
56 PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
58 pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func, pa_free_cb_t key_free_func, pa_free_cb_t value_free_func) {
61 h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*));
63 h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
64 h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
66 h->key_free_func = key_free_func;
67 h->value_free_func = value_free_func;
70 h->iterate_list_head = h->iterate_list_tail = NULL;
75 pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
76 return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL);
79 static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) {
83 /* Remove from iteration list */
85 e->iterate_next->iterate_previous = e->iterate_previous;
87 h->iterate_list_tail = e->iterate_previous;
89 if (e->iterate_previous)
90 e->iterate_previous->iterate_next = e->iterate_next;
92 h->iterate_list_head = e->iterate_next;
94 /* Remove from hash table bucket list */
96 e->bucket_next->bucket_previous = e->bucket_previous;
98 if (e->bucket_previous)
99 e->bucket_previous->bucket_next = e->bucket_next;
101 unsigned hash = h->hash_func(e->key) % NBUCKETS;
102 BY_HASH(h)[hash] = e->bucket_next;
105 if (h->key_free_func)
106 h->key_free_func(e->key);
108 if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
111 pa_assert(h->n_entries >= 1);
115 void pa_hashmap_free(pa_hashmap *h) {
118 pa_hashmap_remove_all(h);
122 static struct hashmap_entry *hash_scan(const pa_hashmap *h, unsigned hash, const void *key) {
123 struct hashmap_entry *e;
125 pa_assert(hash < NBUCKETS);
127 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
128 if (h->compare_func(e->key, key) == 0)
134 int pa_hashmap_put(pa_hashmap *h, void *key, void *value) {
135 struct hashmap_entry *e;
140 hash = h->hash_func(key) % NBUCKETS;
142 if (hash_scan(h, hash, key))
145 if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
146 e = pa_xnew(struct hashmap_entry, 1);
151 /* Insert into hash table */
152 e->bucket_next = BY_HASH(h)[hash];
153 e->bucket_previous = NULL;
154 if (BY_HASH(h)[hash])
155 BY_HASH(h)[hash]->bucket_previous = e;
156 BY_HASH(h)[hash] = e;
158 /* Insert into iteration list */
159 e->iterate_previous = h->iterate_list_tail;
160 e->iterate_next = NULL;
161 if (h->iterate_list_tail) {
162 pa_assert(h->iterate_list_head);
163 h->iterate_list_tail->iterate_next = e;
165 pa_assert(!h->iterate_list_head);
166 h->iterate_list_head = e;
168 h->iterate_list_tail = e;
171 pa_assert(h->n_entries >= 1);
176 void* pa_hashmap_get(const pa_hashmap *h, const void *key) {
178 struct hashmap_entry *e;
182 hash = h->hash_func(key) % NBUCKETS;
184 if (!(e = hash_scan(h, hash, key)))
190 void* pa_hashmap_remove(pa_hashmap *h, const void *key) {
191 struct hashmap_entry *e;
197 hash = h->hash_func(key) % NBUCKETS;
199 if (!(e = hash_scan(h, hash, key)))
208 int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) {
213 data = pa_hashmap_remove(h, key);
215 if (data && h->value_free_func)
216 h->value_free_func(data);
218 return data ? 0 : -1;
221 void pa_hashmap_remove_all(pa_hashmap *h) {
224 while (h->iterate_list_head) {
226 data = h->iterate_list_head->value;
227 remove_entry(h, h->iterate_list_head);
229 if (h->value_free_func)
230 h->value_free_func(data);
234 void *pa_hashmap_iterate(const pa_hashmap *h, void **state, const void **key) {
235 struct hashmap_entry *e;
240 if (*state == (void*) -1)
243 if (!*state && !h->iterate_list_head)
246 e = *state ? *state : h->iterate_list_head;
249 *state = e->iterate_next;
259 *state = (void *) -1;
267 void *pa_hashmap_iterate_backwards(const pa_hashmap *h, void **state, const void **key) {
268 struct hashmap_entry *e;
273 if (*state == (void*) -1)
276 if (!*state && !h->iterate_list_tail)
279 e = *state ? *state : h->iterate_list_tail;
281 if (e->iterate_previous)
282 *state = e->iterate_previous;
292 *state = (void *) -1;
300 void* pa_hashmap_first(const pa_hashmap *h) {
303 if (!h->iterate_list_head)
306 return h->iterate_list_head->value;
309 void* pa_hashmap_last(const pa_hashmap *h) {
312 if (!h->iterate_list_tail)
315 return h->iterate_list_tail->value;
318 void* pa_hashmap_steal_first(pa_hashmap *h) {
323 if (!h->iterate_list_head)
326 data = h->iterate_list_head->value;
327 remove_entry(h, h->iterate_list_head);
332 unsigned pa_hashmap_size(const pa_hashmap *h) {
338 bool pa_hashmap_isempty(const pa_hashmap *h) {
341 return h->n_entries == 0;