#ifndef UC_HTABLE_H_ #define UC_HTABLE_H_ #include /** @file * Building block for hash table. * The hash table is stored using a chained list of buckets. * Nodes with different hash value can be stored in the same bucket. * * * The hash table manages only the hash value, not the equality * between nodes. * * It's up to the user to check for duplicate values * before adding nodes if dublicates are not desired. * * Finding a node must be done in a user specific way, * normally by iterating over all nodes with the desidred * hash, and deciding in some other way whether it is the desidred * node. */ /** Node the hashtable tracks. * Will normally be embedded inside another struct */ struct UHNode { //cached value of the hash size_t hash; //pointer to next in same bucket struct UHNode *next; }; /** * The hash table struct */ struct UHTable { size_t cnt_elements; size_t cnt_buckets; struct UHNode **buckets; }; /** * Initialize a hash table. * @param table hash table to initialize * @param buckets number of buckets * @return 0 on success, errno if malloc fails or buckets == 0 */ int uc_htable_init(struct UHTable *table, size_t buckets); /** * Free resources of the hash table. * @param table hash table to free (does not free @table itself */ void uc_htable_destroy(struct UHTable *table); /** * @param table * @param hash find the bucket from this hash value * @return the bucket number for the given hash */ static inline size_t uc_htable_bucket(const struct UHTable *table, size_t hash) { return hash % table->cnt_buckets; } /** * Get the first bucket node for the given hash. * The returned node might not have the same hash, since different * nodes can share the same bucket. * * @param table table * @param hash hash */ static inline struct UHNode *uc_htable_first_bucket(const struct UHTable *table, size_t hash) { size_t bucket = uc_htable_bucket(table, hash); return table->buckets[bucket]; } /** * Get the next node in the bucket. * @return next node or NULL */ static inline struct UHNode *uc_htable_next_in_bucket(const struct UHNode *node) { return node->next; } /** * Get the first node that have the given hash * @param table table * @param hash hash * @return first node with the given hash or NULL */ struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash); //internal helper struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash); /** * Get the next node with the same hash * @param node previous node * @return next node with the same hash as node or NULL */ static inline struct UHNode *uc_htable_next_hash(const struct UHNode *node) { return uc_htable_next_hash_hlp(node->next, node->hash); } //internal helper struct UHNode *uc_htable_iter_hlp(struct UHTable *table, size_t start_bucket); /** Get the first node in the hash table * * @param table table * @return first node or NULL */ static inline struct UHNode *uc_htable_first(struct UHTable *table) { return uc_htable_iter_hlp(table, 0); } /** Get the next node in the hash table * * @param table table * @param node previous node * @return first node or NULL */ struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node); /** * Swap 2 hash tables. * * @param a first table * @param b second table */ void uc_htable_swap(struct UHTable *a, struct UHTable *b); /** * Iterate over all nodes in a bucket. * The table must not be changed while iterating. * * @param table the struct UHTable* * @param node_iter struct UHNode* for the current node being iterated * @param hash size_t hash for the bucket to iterate */ #define UC_HTABLE_FOREACH_BUCKET(table, node_iter, hash)\ for ((node_iter) = uc_htable_first_bucket((table), (hash));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next_in_bucket((node_iter))) /** * Iterate over all nodes that has the given hash. * The table must not be changed while iterating. * Normally the user would check in a user specific way * if the node_iter is equal to the node one wants to find. * * @param table the struct UHTable* * @param node_iter struct UHNode* for the current node being iterated * @param hash size_t hash for the node(s) to iterate over */ #define UC_HTABLE_FOREACH_HASH(table, node_iter, hash)\ for ((node_iter) = uc_htable_first_hash((table), (hash));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next_hash((node_iter))) /** * "Safe" variant of UC_HTABLE_FOREACH_HASH, the iterated node * (or any previously iterated nodes) can be freed while iterating * * @param table the struct UHTable* * @param node_iter struct UHNode* for the current node being iterated * @param next_iter struct UHNode* for the next node, required by the * iteration, next_iter must not be altered while iterating. * @param hash size_t hash for the node(s) to iterate over */ #define UC_HTABLE_FOREACH_HASH_SAFE(table, node_iter, next_iter, hash)\ for ((node_iter) = uc_htable_first_hash((table), (hash));\ (node_iter) != NULL && (((next_iter) = (node_iter->next)), 1);\ (node_iter) = (next)) /** * Iterate over all nodes in a table * The table must not be changed while iterating. * * @param table the struct UHTable* * @param node_iter struct UHNode* for the current node being iterated */ #define UC_HTABLE_FOREACH(table, node_iter)\ for ((node_iter) = uc_htable_first((table));\ (node_iter) != NULL;\ (node_iter) = uc_htable_next((table), (node_iter))) /** * "Safe" variant of UC_HTABLE_FOREACH, the iterated node * (or any previously iterated nodes) can be freed while iterating * * @param table the struct UHTable* * @param node_iter struct UHNode* for the current node being iterated * @param next_iter struct UHNode* for the next node, required by the * iteration, next_iter must not be altered while iterating. */ #define UC_HTABLE_FOREACH_SAFE(table, node_iter, next_iter)\ for ((node_iter) = uc_htable_first((table));\ (node_iter) != NULL &&\ ((next_iter) = uc_htable_next((table), (node_iter)), 1);\ (node_iter) = (next)) /** * Check if the given node pointer exists in the hash table * * @param table table * @param node node to check for * @return 0 if the node does not exist in table non-0 if it does */ int uc_htable_has_node(struct UHTable *table, struct UHNode *node); /** * * @return 0 if table is empty non-0 if it contains any nodes */ static inline int uc_htable_isempty(const struct UHTable *table) { return table->cnt_elements == 0; } /** * Get number of elements in the hash table. * * @rparam table table * @return number of elements */ static inline size_t uc_htable_count(const struct UHTable *table) { return table->cnt_elements; } /** Clears all nodes from the hash table, making it empty. * No memory is released. * @param table table to clear */ void uc_htable_clear(struct UHTable *table); /** Insert a new node. * The new node pointer must exist as long as it is part of the * hash table. The hash table does not check for duplicates in any way. * * @param table table to insert into * @param node new node to insert. * @param hash hash value of the new node */ void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash); /** Remove a node. * If the node pointer is not part of the table, * no action is taken * * @param table table to remove from. * @param node node to remove. */ void uc_htable_remove(struct UHTable *table, struct UHNode *node); /** Resize the hash table. This changes the number of buckets, and re-inserts * all nodes. * * @param table table to resize * @param buckets new number of buckets, must be > 0 * @return 0 on success, errno if malloc fails or buckets == 0 */ int uc_htable_resize(struct UHTable *table, size_t buckets); #endif