#ifndef UC_HTABLE_H_ #define UC_HTABLE_H_ #include /** 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; }; /** Iterator for the iterator API */ struct UHIter { //internal fields struct UHNode **it; struct UHNode **next; size_t cur_bucket; size_t cur_hash; }; /** * 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_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 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_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))) /** * 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))) /** * 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); /** Get the first node with the given hash. * * @param table table * @param iter opaque iterator * @param hash hash to find * @return the first node found or NULL */ struct UHNode *uc_htable_iter_first_hash(struct UHTable *table, struct UHIter *iter, size_t hash); /** Get the next node with the given hash, the iterator * must previously have been passed to uc_htable_iter_first_hash * which returned a non-NULL value * * @param iter opaque iterator * @return the next node found or NULL */ struct UHNode *uc_htable_iter_next_hash(struct UHIter *iter); /** Remove the node pointed to by the current iterator, * uc_htable_iter_first_hash or uc_htable_iter_next_hash * must previously have returned non-NULL. The node * returned by the previous call is the one removed. * It is safe to continue iterating after this * * @param iter indicated node to remove */ void uc_htable_iter_remove(struct UHIter *iter); #endif