| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733 |
- /*
- *
- * Embedded Linux library
- *
- * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- *
- * This library 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 this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include "hashmap.h"
- #include "private.h"
- #include "useful.h"
- /**
- * SECTION:hashmap
- * @short_description: Hash table support
- *
- * Hash table support
- */
- #define NBUCKETS 127
- struct entry {
- void *key;
- void *value;
- struct entry *next;
- unsigned int hash;
- };
- /**
- * l_hashmap:
- *
- * Opague object representing the hash table.
- */
- struct l_hashmap {
- l_hashmap_hash_func_t hash_func;
- l_hashmap_compare_func_t compare_func;
- l_hashmap_key_new_func_t key_new_func;
- l_hashmap_key_free_func_t key_free_func;
- unsigned int entries;
- struct entry buckets[NBUCKETS];
- };
- static inline void *get_key_new(const struct l_hashmap *hashmap,
- const void *key)
- {
- if (hashmap->key_new_func)
- return hashmap->key_new_func(key);
- return (void *)key;
- }
- static inline void free_key(const struct l_hashmap *hashmap, void *key)
- {
- if (hashmap->key_free_func)
- hashmap->key_free_func(key);
- }
- static inline unsigned int hash_superfast(const uint8_t *key, unsigned int len)
- {
- /*
- * Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
- * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/),
- * EFL's eina, kmod and possible others.
- */
- unsigned int tmp, hash = len, rem = len & 3;
- len /= 4;
- /* Main loop */
- for (; len > 0; len--) {
- hash += l_get_u16(key);
- tmp = (l_get_u16(key + 2) << 11) ^ hash;
- hash = (hash << 16) ^ tmp;
- key += 4;
- hash += hash >> 11;
- }
- /* Handle end cases */
- switch (rem) {
- case 3:
- hash += l_get_u16(key);
- hash ^= hash << 16;
- hash ^= key[2] << 18;
- hash += hash >> 11;
- break;
- case 2:
- hash += l_get_u16(key);
- hash ^= hash << 11;
- hash += hash >> 17;
- break;
- case 1:
- hash += *key;
- hash ^= hash << 10;
- hash += hash >> 1;
- break;
- }
- /* Force "avalanching" of final 127 bits */
- hash ^= hash << 3;
- hash += hash >> 5;
- hash ^= hash << 4;
- hash += hash >> 17;
- hash ^= hash << 25;
- hash += hash >> 6;
- return hash;
- }
- static unsigned int direct_hash_func(const void *p)
- {
- return L_PTR_TO_UINT(p);
- }
- static int direct_compare_func(const void *a, const void *b)
- {
- return a < b ? -1 : (a > b ? 1 : 0);
- }
- /**
- * l_hashmap_new:
- *
- * Create a new hash table. The keys are managed as pointers, that is,
- * the pointer value is hashed and looked up.
- *
- * No error handling is needed since. In case of real memory allocation
- * problems abort() will be called.
- *
- * See also l_hashmap_string_new().
- *
- * Returns: a newly allocated #l_hashmap object
- **/
- LIB_EXPORT struct l_hashmap *l_hashmap_new(void)
- {
- struct l_hashmap *hashmap;
- hashmap = l_new(struct l_hashmap, 1);
- hashmap->hash_func = direct_hash_func;
- hashmap->compare_func = direct_compare_func;
- hashmap->entries = 0;
- return hashmap;
- }
- LIB_EXPORT unsigned int l_str_hash(const void *p)
- {
- const char *s = p;
- size_t len = strlen(s);
- return hash_superfast((const uint8_t *)s, len);
- }
- /**
- * l_hashmap_string_new:
- *
- * Create a new hash table. The keys are considered strings and are
- * copied.
- *
- * No error handling is needed since. In case of real memory allocation
- * problems abort() will be called.
- *
- * See also l_hashmap_new().
- *
- * Returns: a newly allocated #l_hashmap object
- **/
- LIB_EXPORT struct l_hashmap *l_hashmap_string_new(void)
- {
- struct l_hashmap *hashmap;
- hashmap = l_new(struct l_hashmap, 1);
- hashmap->hash_func = l_str_hash;
- hashmap->compare_func = (l_hashmap_compare_func_t) strcmp;
- hashmap->key_new_func = (l_hashmap_key_new_func_t) l_strdup;
- hashmap->key_free_func = l_free;
- hashmap->entries = 0;
- return hashmap;
- }
- /**
- * l_hashmap_set_hash_function:
- * @hashmap: hash table object
- * @func: Key hashing function
- *
- * Sets the hashing function to be used by this object.
- *
- * This function can only be called when the @hashmap is empty.
- *
- * Returns: #true when the hashing function could be updated successfully,
- * and #false otherwise.
- **/
- LIB_EXPORT bool l_hashmap_set_hash_function(struct l_hashmap *hashmap,
- l_hashmap_hash_func_t func)
- {
- if (unlikely(!hashmap))
- return false;
- if (hashmap->entries != 0)
- return false;
- hashmap->hash_func = func;
- return true;
- }
- /**
- * l_hashmap_set_compare_function:
- * @hashmap: hash table object
- * @func: Key compare function
- *
- * Sets the key comparison function to be used by this object.
- *
- * This function can only be called when the @hashmap is empty.
- *
- * Returns: #true when the comparison function could be updated successfully,
- * and #false otherwise.
- **/
- LIB_EXPORT bool l_hashmap_set_compare_function(struct l_hashmap *hashmap,
- l_hashmap_compare_func_t func)
- {
- if (unlikely(!hashmap))
- return false;
- if (hashmap->entries != 0)
- return false;
- hashmap->compare_func = func;
- return true;
- }
- /**
- * l_hashmap_set_key_copy_function:
- * @hashmap: hash table object
- * @func: Key duplication function
- *
- * Sets the key duplication function to be used by this object. If the
- * function is NULL, then the keys are assigned directly.
- *
- * This function can only be called when the @hashmap is empty.
- *
- * Returns: #true when the key copy function could be updated successfully,
- * and #false otherwise.
- **/
- LIB_EXPORT bool l_hashmap_set_key_copy_function(struct l_hashmap *hashmap,
- l_hashmap_key_new_func_t func)
- {
- if (unlikely(!hashmap))
- return false;
- if (hashmap->entries != 0)
- return false;
- hashmap->key_new_func = func;
- return true;
- }
- /**
- * l_hashmap_set_key_free_function:
- * @hashmap: hash table object
- * @func: Key destructor function
- *
- * Sets the key destructor function to be used by this object. This function
- * should undo the result of the function specified in
- * l_hashmap_set_key_copy_function(). This function can be NULL, in which
- * case no destructor is called.
- *
- * This function can only be called when the @hashmap is empty.
- *
- * Returns: #true when the key free function could be updated successfully,
- * and #false otherwise.
- **/
- LIB_EXPORT bool l_hashmap_set_key_free_function(struct l_hashmap *hashmap,
- l_hashmap_key_free_func_t func)
- {
- if (unlikely(!hashmap))
- return false;
- if (hashmap->entries != 0)
- return false;
- hashmap->key_free_func = func;
- return true;
- }
- /**
- * l_hashmap_destroy:
- * @hashmap: hash table object
- * @destroy: destroy function
- *
- * Free hash table and call @destory on all remaining entries.
- *
- * NOTE: While the destroy is in progress, the hashmap is assumed to be
- * invariant. The behavior of adding or removing entries while a destroy
- * operation is in progress is undefined.
- **/
- LIB_EXPORT void l_hashmap_destroy(struct l_hashmap *hashmap,
- l_hashmap_destroy_func_t destroy)
- {
- unsigned int i;
- if (unlikely(!hashmap))
- return;
- for (i = 0; i < NBUCKETS; i++) {
- struct entry *entry, *next, *head = &hashmap->buckets[i];
- if (!head->next)
- continue;
- for (entry = head;; entry = next) {
- if (destroy)
- destroy(entry->value);
- free_key(hashmap, entry->key);
- next = entry->next;
- if (entry != head)
- l_free(entry);
- if (next == head)
- break;
- }
- }
- l_free(hashmap);
- }
- /**
- * l_hashmap_insert:
- * @hashmap: hash table object
- * @key: key pointer
- * @value: value pointer
- *
- * Insert new @value entry with @key. Note that entries with a duplicate key
- * are allowed. If a duplicate entry in inserted, it will be added in order
- * of insertion. @l_hashmap_lookup and @l_hashmap_remove will remove the
- * first matching entry.
- *
- * Returns: #true when value has been added and #false in case of failure
- **/
- LIB_EXPORT bool l_hashmap_insert(struct l_hashmap *hashmap,
- const void *key, void *value)
- {
- struct entry *entry, *head;
- unsigned int hash;
- void *key_new;
- if (unlikely(!hashmap))
- return false;
- key_new = get_key_new(hashmap, key);
- hash = hashmap->hash_func(key_new);
- head = &hashmap->buckets[hash % NBUCKETS];
- if (!head->next) {
- head->key = key_new;
- head->value = value;
- head->hash = hash;
- head->next = head;
- goto done;
- }
- entry = l_new(struct entry, 1);
- entry->key = key_new;
- entry->value = value;
- entry->hash = hash;
- entry->next = head;
- while (head->next != entry->next)
- head = head->next;
- head->next = entry;
- done:
- hashmap->entries++;
- return true;
- }
- /**
- * l_hashmap_replace:
- * @hashmap: hash table object
- * @key: key pointer
- * @value: value pointer
- * @old_value: old value that has been replaced.
- *
- * Replace the first entry with @key by @value or insert a new @value entry
- * with @key. If the entry was replaced, then the old value is returned
- * in @old_value, otherwise @old_value is assigned a #NULL.
- *
- * Returns: #true when value has been added and #false in case of failure
- **/
- LIB_EXPORT bool l_hashmap_replace(struct l_hashmap *hashmap,
- const void *key, void *value,
- void **old_value)
- {
- struct entry *entry;
- struct entry *head;
- unsigned int hash;
- void *key_new;
- if (unlikely(!hashmap))
- return false;
- key_new = get_key_new(hashmap, key);
- hash = hashmap->hash_func(key_new);
- head = &hashmap->buckets[hash % NBUCKETS];
- if (!head->next) {
- head->key = key_new;
- head->value = value;
- head->hash = hash;
- head->next = head;
- goto done;
- }
- for (entry = head;; entry = entry->next) {
- if (entry->hash != hash)
- goto next;
- if (hashmap->compare_func(key, entry->key))
- goto next;
- if (old_value)
- *old_value = entry->value;
- entry->value = value;
- free_key(hashmap, key_new);
- return true;
- next:
- if (entry->next == head)
- break;
- }
- entry = l_new(struct entry, 1);
- entry->key = key_new;
- entry->value = value;
- entry->hash = hash;
- entry->next = head;
- while (head->next != entry->next)
- head = head->next;
- head->next = entry;
- done:
- if (old_value)
- *old_value = NULL;
- hashmap->entries++;
- return true;
- }
- /**
- * l_hashmap_remove:
- * @hashmap: hash table object
- * @key: key pointer
- *
- * Remove entry for @key.
- *
- * Returns: value pointer of the removed entry or #NULL in case of failure
- **/
- LIB_EXPORT void *l_hashmap_remove(struct l_hashmap *hashmap, const void *key)
- {
- struct entry *entry, *head, *prev;
- unsigned int hash;
- if (unlikely(!hashmap))
- return NULL;
- hash = hashmap->hash_func(key);
- head = &hashmap->buckets[hash % NBUCKETS];
- if (!head->next)
- return NULL;
- for (entry = head, prev = NULL;; prev = entry, entry = entry->next) {
- void *value;
- if (entry->hash != hash)
- goto next;
- if (hashmap->compare_func(key, entry->key))
- goto next;
- value = entry->value;
- if (entry == head) {
- if (entry->next == head) {
- free_key(hashmap, entry->key);
- head->key = NULL;
- head->value = NULL;
- head->hash = 0;
- head->next = NULL;
- } else {
- entry = entry->next;
- free_key(hashmap, head->key);
- head->key = entry->key;
- head->value = entry->value;
- head->hash = entry->hash;
- head->next = entry->next;
- l_free(entry);
- }
- } else {
- prev->next = entry->next;
- free_key(hashmap, entry->key);
- l_free(entry);
- }
- hashmap->entries--;
- return value;
- next:
- if (entry->next == head)
- break;
- }
- return NULL;
- }
- /**
- * l_hashmap_lookup:
- * @hashmap: hash table object
- * @key: key pointer
- *
- * Lookup entry for @key.
- *
- * Returns: value pointer for @key or #NULL in case of failure
- **/
- LIB_EXPORT void *l_hashmap_lookup(struct l_hashmap *hashmap, const void *key)
- {
- struct entry *entry, *head;
- unsigned int hash;
- if (unlikely(!hashmap))
- return NULL;
- hash = hashmap->hash_func(key);
- head = &hashmap->buckets[hash % NBUCKETS];
- if (!head->next)
- return NULL;
- for (entry = head;; entry = entry->next) {
- if (entry->hash == hash &&
- !hashmap->compare_func(key, entry->key))
- return entry->value;
- if (entry->next == head)
- break;
- }
- return NULL;
- }
- /**
- * l_hashmap_foreach:
- * @hashmap: hash table object
- * @function: callback function
- * @user_data: user data given to callback function
- *
- * Call @function for every entry in @hashmap.
- *
- * NOTE: While the foreach is in progress, the hashmap is assumed to be
- * invariant. The behavior of adding or removing entries while a foreach
- * operation is in progress is undefined.
- **/
- LIB_EXPORT void l_hashmap_foreach(struct l_hashmap *hashmap,
- l_hashmap_foreach_func_t function, void *user_data)
- {
- unsigned int i;
- if (unlikely(!hashmap || !function))
- return;
- for (i = 0; i < NBUCKETS; i++) {
- struct entry *entry, *head = &hashmap->buckets[i];
- if (!head->next)
- continue;
- for (entry = head;; entry = entry->next) {
- function(entry->key, entry->value, user_data);
- if (entry->next == head)
- break;
- }
- }
- }
- /**
- * l_hashmap_foreach_remove:
- * @hashmap: hash table object
- * @function: callback function
- * @user_data: user data given to callback function
- *
- * Call @function for every entry in @hashmap. If the @function returns
- * true, then the object will be removed from the hashmap.
- *
- * NOTE: While the foreach is in progress, the hashmap is assumed to be
- * invariant. The behavior of adding or removing entries while a foreach
- * operation is in progress is undefined.
- *
- * Returns: Number of entries removed.
- **/
- LIB_EXPORT unsigned int l_hashmap_foreach_remove(struct l_hashmap *hashmap,
- l_hashmap_remove_func_t function,
- void *user_data)
- {
- unsigned int i;
- unsigned int nremoved = 0;
- if (unlikely(!hashmap || !function))
- return 0;
- for (i = 0; i < NBUCKETS; i++) {
- struct entry *head = &hashmap->buckets[i];
- struct entry *entry;
- struct entry *prev;
- bool remove;
- if (head->next == NULL)
- continue;
- entry = head;
- prev = NULL;
- while (true) {
- remove = function(entry->key, entry->value, user_data);
- if (!remove)
- goto next;
- nremoved += 1;
- hashmap->entries -= 1;
- if (entry == head) {
- if (entry->next == head) {
- free_key(hashmap, entry->key);
- head->key = NULL;
- head->value = NULL;
- head->hash = 0;
- head->next = NULL;
- break;
- } else {
- entry = entry->next;
- free_key(hashmap, head->key);
- head->key = entry->key;
- head->value = entry->value;
- head->hash = entry->hash;
- head->next = entry->next;
- l_free(entry);
- entry = head;
- continue;
- }
- } else {
- prev->next = entry->next;
- free_key(hashmap, entry->key);
- l_free(entry);
- entry = prev->next;
- if (entry == head)
- break;
- continue;
- }
- next:
- if (entry->next == head)
- break;
- prev = entry;
- entry = entry->next;
- }
- }
- return nremoved;
- }
- /**
- * l_hashmap_size:
- * @hashmap: hash table object
- *
- * Returns: entries in the hash table
- **/
- LIB_EXPORT unsigned int l_hashmap_size(struct l_hashmap *hashmap)
- {
- if (unlikely(!hashmap))
- return 0;
- return hashmap->entries;
- }
- /**
- * l_hashmap_isempty:
- * @hashmap: hash table object
- *
- * Returns: #true if hash table is empty and #false if not
- **/
- LIB_EXPORT bool l_hashmap_isempty(struct l_hashmap *hashmap)
- {
- if (unlikely(!hashmap))
- return true;
- return hashmap->entries == 0;
- }
|