153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdlib.h> 2553a5a1b3Sopenharmony_ci 2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2753a5a1b3Sopenharmony_ci#include <pulsecore/idxset.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/flist.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include "hashmap.h" 3253a5a1b3Sopenharmony_ci 3353a5a1b3Sopenharmony_ci#define NBUCKETS 127 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_cistruct hashmap_entry { 3653a5a1b3Sopenharmony_ci void *key; 3753a5a1b3Sopenharmony_ci void *value; 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ci struct hashmap_entry *bucket_next, *bucket_previous; 4053a5a1b3Sopenharmony_ci struct hashmap_entry *iterate_next, *iterate_previous; 4153a5a1b3Sopenharmony_ci}; 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_cistruct pa_hashmap { 4453a5a1b3Sopenharmony_ci pa_hash_func_t hash_func; 4553a5a1b3Sopenharmony_ci pa_compare_func_t compare_func; 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_ci pa_free_cb_t key_free_func; 4853a5a1b3Sopenharmony_ci pa_free_cb_t value_free_func; 4953a5a1b3Sopenharmony_ci 5053a5a1b3Sopenharmony_ci struct hashmap_entry *iterate_list_head, *iterate_list_tail; 5153a5a1b3Sopenharmony_ci unsigned n_entries; 5253a5a1b3Sopenharmony_ci}; 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + PA_ALIGN(sizeof(pa_hashmap)))) 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_ciPA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree); 5753a5a1b3Sopenharmony_ci 5853a5a1b3Sopenharmony_cipa_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) { 5953a5a1b3Sopenharmony_ci pa_hashmap *h; 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_ci h = pa_xmalloc0(PA_ALIGN(sizeof(pa_hashmap)) + NBUCKETS*sizeof(struct hashmap_entry*)); 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_ci h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; 6453a5a1b3Sopenharmony_ci h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_ci h->key_free_func = key_free_func; 6753a5a1b3Sopenharmony_ci h->value_free_func = value_free_func; 6853a5a1b3Sopenharmony_ci 6953a5a1b3Sopenharmony_ci h->n_entries = 0; 7053a5a1b3Sopenharmony_ci h->iterate_list_head = h->iterate_list_tail = NULL; 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci return h; 7353a5a1b3Sopenharmony_ci} 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_cipa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) { 7653a5a1b3Sopenharmony_ci return pa_hashmap_new_full(hash_func, compare_func, NULL, NULL); 7753a5a1b3Sopenharmony_ci} 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_cistatic void remove_entry(pa_hashmap *h, struct hashmap_entry *e) { 8053a5a1b3Sopenharmony_ci pa_assert(h); 8153a5a1b3Sopenharmony_ci pa_assert(e); 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci /* Remove from iteration list */ 8453a5a1b3Sopenharmony_ci if (e->iterate_next) 8553a5a1b3Sopenharmony_ci e->iterate_next->iterate_previous = e->iterate_previous; 8653a5a1b3Sopenharmony_ci else 8753a5a1b3Sopenharmony_ci h->iterate_list_tail = e->iterate_previous; 8853a5a1b3Sopenharmony_ci 8953a5a1b3Sopenharmony_ci if (e->iterate_previous) 9053a5a1b3Sopenharmony_ci e->iterate_previous->iterate_next = e->iterate_next; 9153a5a1b3Sopenharmony_ci else 9253a5a1b3Sopenharmony_ci h->iterate_list_head = e->iterate_next; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci /* Remove from hash table bucket list */ 9553a5a1b3Sopenharmony_ci if (e->bucket_next) 9653a5a1b3Sopenharmony_ci e->bucket_next->bucket_previous = e->bucket_previous; 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci if (e->bucket_previous) 9953a5a1b3Sopenharmony_ci e->bucket_previous->bucket_next = e->bucket_next; 10053a5a1b3Sopenharmony_ci else { 10153a5a1b3Sopenharmony_ci unsigned hash = h->hash_func(e->key) % NBUCKETS; 10253a5a1b3Sopenharmony_ci BY_HASH(h)[hash] = e->bucket_next; 10353a5a1b3Sopenharmony_ci } 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ci if (h->key_free_func) 10653a5a1b3Sopenharmony_ci h->key_free_func(e->key); 10753a5a1b3Sopenharmony_ci 10853a5a1b3Sopenharmony_ci if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0) 10953a5a1b3Sopenharmony_ci pa_xfree(e); 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci pa_assert(h->n_entries >= 1); 11253a5a1b3Sopenharmony_ci h->n_entries--; 11353a5a1b3Sopenharmony_ci} 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_civoid pa_hashmap_free(pa_hashmap *h) { 11653a5a1b3Sopenharmony_ci pa_assert(h); 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci pa_hashmap_remove_all(h); 11953a5a1b3Sopenharmony_ci pa_xfree(h); 12053a5a1b3Sopenharmony_ci} 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_cistatic struct hashmap_entry *hash_scan(const pa_hashmap *h, unsigned hash, const void *key) { 12353a5a1b3Sopenharmony_ci struct hashmap_entry *e; 12453a5a1b3Sopenharmony_ci pa_assert(h); 12553a5a1b3Sopenharmony_ci pa_assert(hash < NBUCKETS); 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci for (e = BY_HASH(h)[hash]; e; e = e->bucket_next) 12853a5a1b3Sopenharmony_ci if (h->compare_func(e->key, key) == 0) 12953a5a1b3Sopenharmony_ci return e; 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci return NULL; 13253a5a1b3Sopenharmony_ci} 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ciint pa_hashmap_put(pa_hashmap *h, void *key, void *value) { 13553a5a1b3Sopenharmony_ci struct hashmap_entry *e; 13653a5a1b3Sopenharmony_ci unsigned hash; 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci pa_assert(h); 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci hash = h->hash_func(key) % NBUCKETS; 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_ci if (hash_scan(h, hash, key)) 14353a5a1b3Sopenharmony_ci return -1; 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries)))) 14653a5a1b3Sopenharmony_ci e = pa_xnew(struct hashmap_entry, 1); 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci e->key = key; 14953a5a1b3Sopenharmony_ci e->value = value; 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci /* Insert into hash table */ 15253a5a1b3Sopenharmony_ci e->bucket_next = BY_HASH(h)[hash]; 15353a5a1b3Sopenharmony_ci e->bucket_previous = NULL; 15453a5a1b3Sopenharmony_ci if (BY_HASH(h)[hash]) 15553a5a1b3Sopenharmony_ci BY_HASH(h)[hash]->bucket_previous = e; 15653a5a1b3Sopenharmony_ci BY_HASH(h)[hash] = e; 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci /* Insert into iteration list */ 15953a5a1b3Sopenharmony_ci e->iterate_previous = h->iterate_list_tail; 16053a5a1b3Sopenharmony_ci e->iterate_next = NULL; 16153a5a1b3Sopenharmony_ci if (h->iterate_list_tail) { 16253a5a1b3Sopenharmony_ci pa_assert(h->iterate_list_head); 16353a5a1b3Sopenharmony_ci h->iterate_list_tail->iterate_next = e; 16453a5a1b3Sopenharmony_ci } else { 16553a5a1b3Sopenharmony_ci pa_assert(!h->iterate_list_head); 16653a5a1b3Sopenharmony_ci h->iterate_list_head = e; 16753a5a1b3Sopenharmony_ci } 16853a5a1b3Sopenharmony_ci h->iterate_list_tail = e; 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_ci h->n_entries++; 17153a5a1b3Sopenharmony_ci pa_assert(h->n_entries >= 1); 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci return 0; 17453a5a1b3Sopenharmony_ci} 17553a5a1b3Sopenharmony_ci 17653a5a1b3Sopenharmony_civoid* pa_hashmap_get(const pa_hashmap *h, const void *key) { 17753a5a1b3Sopenharmony_ci unsigned hash; 17853a5a1b3Sopenharmony_ci struct hashmap_entry *e; 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_ci pa_assert(h); 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci hash = h->hash_func(key) % NBUCKETS; 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci if (!(e = hash_scan(h, hash, key))) 18553a5a1b3Sopenharmony_ci return NULL; 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci return e->value; 18853a5a1b3Sopenharmony_ci} 18953a5a1b3Sopenharmony_ci 19053a5a1b3Sopenharmony_civoid* pa_hashmap_remove(pa_hashmap *h, const void *key) { 19153a5a1b3Sopenharmony_ci struct hashmap_entry *e; 19253a5a1b3Sopenharmony_ci unsigned hash; 19353a5a1b3Sopenharmony_ci void *data; 19453a5a1b3Sopenharmony_ci 19553a5a1b3Sopenharmony_ci pa_assert(h); 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_ci hash = h->hash_func(key) % NBUCKETS; 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci if (!(e = hash_scan(h, hash, key))) 20053a5a1b3Sopenharmony_ci return NULL; 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_ci data = e->value; 20353a5a1b3Sopenharmony_ci remove_entry(h, e); 20453a5a1b3Sopenharmony_ci 20553a5a1b3Sopenharmony_ci return data; 20653a5a1b3Sopenharmony_ci} 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ciint pa_hashmap_remove_and_free(pa_hashmap *h, const void *key) { 20953a5a1b3Sopenharmony_ci void *data; 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci pa_assert(h); 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_ci data = pa_hashmap_remove(h, key); 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci if (data && h->value_free_func) 21653a5a1b3Sopenharmony_ci h->value_free_func(data); 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci return data ? 0 : -1; 21953a5a1b3Sopenharmony_ci} 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_civoid pa_hashmap_remove_all(pa_hashmap *h) { 22253a5a1b3Sopenharmony_ci pa_assert(h); 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci while (h->iterate_list_head) { 22553a5a1b3Sopenharmony_ci void *data; 22653a5a1b3Sopenharmony_ci data = h->iterate_list_head->value; 22753a5a1b3Sopenharmony_ci remove_entry(h, h->iterate_list_head); 22853a5a1b3Sopenharmony_ci 22953a5a1b3Sopenharmony_ci if (h->value_free_func) 23053a5a1b3Sopenharmony_ci h->value_free_func(data); 23153a5a1b3Sopenharmony_ci } 23253a5a1b3Sopenharmony_ci} 23353a5a1b3Sopenharmony_ci 23453a5a1b3Sopenharmony_civoid *pa_hashmap_iterate(const pa_hashmap *h, void **state, const void **key) { 23553a5a1b3Sopenharmony_ci struct hashmap_entry *e; 23653a5a1b3Sopenharmony_ci 23753a5a1b3Sopenharmony_ci pa_assert(h); 23853a5a1b3Sopenharmony_ci pa_assert(state); 23953a5a1b3Sopenharmony_ci 24053a5a1b3Sopenharmony_ci if (*state == (void*) -1) 24153a5a1b3Sopenharmony_ci goto at_end; 24253a5a1b3Sopenharmony_ci 24353a5a1b3Sopenharmony_ci if (!*state && !h->iterate_list_head) 24453a5a1b3Sopenharmony_ci goto at_end; 24553a5a1b3Sopenharmony_ci 24653a5a1b3Sopenharmony_ci e = *state ? *state : h->iterate_list_head; 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_ci if (e->iterate_next) 24953a5a1b3Sopenharmony_ci *state = e->iterate_next; 25053a5a1b3Sopenharmony_ci else 25153a5a1b3Sopenharmony_ci *state = (void*) -1; 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci if (key) 25453a5a1b3Sopenharmony_ci *key = e->key; 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci return e->value; 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ciat_end: 25953a5a1b3Sopenharmony_ci *state = (void *) -1; 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_ci if (key) 26253a5a1b3Sopenharmony_ci *key = NULL; 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci return NULL; 26553a5a1b3Sopenharmony_ci} 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_civoid *pa_hashmap_iterate_backwards(const pa_hashmap *h, void **state, const void **key) { 26853a5a1b3Sopenharmony_ci struct hashmap_entry *e; 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci pa_assert(h); 27153a5a1b3Sopenharmony_ci pa_assert(state); 27253a5a1b3Sopenharmony_ci 27353a5a1b3Sopenharmony_ci if (*state == (void*) -1) 27453a5a1b3Sopenharmony_ci goto at_beginning; 27553a5a1b3Sopenharmony_ci 27653a5a1b3Sopenharmony_ci if (!*state && !h->iterate_list_tail) 27753a5a1b3Sopenharmony_ci goto at_beginning; 27853a5a1b3Sopenharmony_ci 27953a5a1b3Sopenharmony_ci e = *state ? *state : h->iterate_list_tail; 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ci if (e->iterate_previous) 28253a5a1b3Sopenharmony_ci *state = e->iterate_previous; 28353a5a1b3Sopenharmony_ci else 28453a5a1b3Sopenharmony_ci *state = (void*) -1; 28553a5a1b3Sopenharmony_ci 28653a5a1b3Sopenharmony_ci if (key) 28753a5a1b3Sopenharmony_ci *key = e->key; 28853a5a1b3Sopenharmony_ci 28953a5a1b3Sopenharmony_ci return e->value; 29053a5a1b3Sopenharmony_ci 29153a5a1b3Sopenharmony_ciat_beginning: 29253a5a1b3Sopenharmony_ci *state = (void *) -1; 29353a5a1b3Sopenharmony_ci 29453a5a1b3Sopenharmony_ci if (key) 29553a5a1b3Sopenharmony_ci *key = NULL; 29653a5a1b3Sopenharmony_ci 29753a5a1b3Sopenharmony_ci return NULL; 29853a5a1b3Sopenharmony_ci} 29953a5a1b3Sopenharmony_ci 30053a5a1b3Sopenharmony_civoid* pa_hashmap_first(const pa_hashmap *h) { 30153a5a1b3Sopenharmony_ci pa_assert(h); 30253a5a1b3Sopenharmony_ci 30353a5a1b3Sopenharmony_ci if (!h->iterate_list_head) 30453a5a1b3Sopenharmony_ci return NULL; 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci return h->iterate_list_head->value; 30753a5a1b3Sopenharmony_ci} 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_civoid* pa_hashmap_last(const pa_hashmap *h) { 31053a5a1b3Sopenharmony_ci pa_assert(h); 31153a5a1b3Sopenharmony_ci 31253a5a1b3Sopenharmony_ci if (!h->iterate_list_tail) 31353a5a1b3Sopenharmony_ci return NULL; 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_ci return h->iterate_list_tail->value; 31653a5a1b3Sopenharmony_ci} 31753a5a1b3Sopenharmony_ci 31853a5a1b3Sopenharmony_civoid* pa_hashmap_steal_first(pa_hashmap *h) { 31953a5a1b3Sopenharmony_ci void *data; 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci pa_assert(h); 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci if (!h->iterate_list_head) 32453a5a1b3Sopenharmony_ci return NULL; 32553a5a1b3Sopenharmony_ci 32653a5a1b3Sopenharmony_ci data = h->iterate_list_head->value; 32753a5a1b3Sopenharmony_ci remove_entry(h, h->iterate_list_head); 32853a5a1b3Sopenharmony_ci 32953a5a1b3Sopenharmony_ci return data; 33053a5a1b3Sopenharmony_ci} 33153a5a1b3Sopenharmony_ci 33253a5a1b3Sopenharmony_ciunsigned pa_hashmap_size(const pa_hashmap *h) { 33353a5a1b3Sopenharmony_ci pa_assert(h); 33453a5a1b3Sopenharmony_ci 33553a5a1b3Sopenharmony_ci return h->n_entries; 33653a5a1b3Sopenharmony_ci} 33753a5a1b3Sopenharmony_ci 33853a5a1b3Sopenharmony_cibool pa_hashmap_isempty(const pa_hashmap *h) { 33953a5a1b3Sopenharmony_ci pa_assert(h); 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci return h->n_entries == 0; 34253a5a1b3Sopenharmony_ci} 343