First iteration of hash table
This commit is contained in:
@@ -0,0 +1,214 @@
|
|||||||
|
#ifndef UC_HTABLE_H_
|
||||||
|
#define UC_HTABLE_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees 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_next_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_next_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);
|
||||||
|
|
||||||
|
#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)))
|
||||||
|
|
||||||
|
#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)))
|
||||||
|
|
||||||
|
#define UC_HTABLE_FOREACH(table, node_iter, hash)\
|
||||||
|
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);
|
||||||
|
|
||||||
|
/** 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
|
||||||
+149
@@ -0,0 +1,149 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ucore/htable.h"
|
||||||
|
|
||||||
|
int uc_htable_init(struct UHTable *table, size_t buckets)
|
||||||
|
{
|
||||||
|
if (buckets == 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
table->buckets = calloc(buckets, sizeof *table->buckets);
|
||||||
|
if (table->buckets == NULL) {
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
table->cnt_buckets = buckets;
|
||||||
|
table->cnt_elements = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_htable_destroy(struct UHTable *table)
|
||||||
|
{
|
||||||
|
free(table->buckets);
|
||||||
|
table->buckets = NULL;
|
||||||
|
table->cnt_elements = table->cnt_buckets = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash)
|
||||||
|
{
|
||||||
|
size_t bucket = uc_htable_bucket(table, hash);
|
||||||
|
struct UHNode *it = table->buckets[bucket];
|
||||||
|
|
||||||
|
while (it && it->hash != hash) {
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash)
|
||||||
|
{
|
||||||
|
while (next && next->hash != hash) {
|
||||||
|
next = next->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UHNode *uc_htable_next_hlp(struct UHTable *table, size_t bucket)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
struct UHNode *found = NULL;
|
||||||
|
|
||||||
|
for (i = bucket; i < table->cnt_buckets; i++) {
|
||||||
|
found = uc_htable_first_bucket(table, i);
|
||||||
|
if (found != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node)
|
||||||
|
{
|
||||||
|
struct UHNode *found;
|
||||||
|
|
||||||
|
found = uc_htable_next_bucket(node);
|
||||||
|
if (found == NULL) {
|
||||||
|
found = uc_htable_next_hlp(table, node->hash + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_htable_swap(struct UHTable *a, struct UHTable *b)
|
||||||
|
{
|
||||||
|
struct UHTable tmp;
|
||||||
|
|
||||||
|
tmp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_htable_has_node(struct UHTable *table, struct UHNode *node)
|
||||||
|
{
|
||||||
|
struct UHNode *iter;
|
||||||
|
|
||||||
|
UC_HTABLE_FOREACH_BUCKET(table, iter, node->hash) {
|
||||||
|
if (node == iter) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_htable_clear(struct UHTable *table)
|
||||||
|
{
|
||||||
|
memset(table->buckets, 0 , sizeof *table->buckets * table->cnt_buckets);
|
||||||
|
table->cnt_elements = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash)
|
||||||
|
{
|
||||||
|
size_t bucket = uc_htable_bucket(table, hash);
|
||||||
|
|
||||||
|
node->next = table->buckets[bucket];
|
||||||
|
table->buckets[bucket] = node;
|
||||||
|
node->hash = hash;
|
||||||
|
|
||||||
|
table->cnt_elements++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uc_htable_resize(struct UHTable *table, size_t buckets)
|
||||||
|
{
|
||||||
|
struct UHTable tmp_table;
|
||||||
|
int rc;
|
||||||
|
size_t h;
|
||||||
|
|
||||||
|
if (buckets == 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = uc_htable_init(&tmp_table, buckets);
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (h = 0; h < table->cnt_buckets; h++) {
|
||||||
|
struct UHNode *next;
|
||||||
|
struct UHNode *node;
|
||||||
|
|
||||||
|
for (node = uc_htable_first_bucket(table, h);
|
||||||
|
node != NULL;
|
||||||
|
node = next) {
|
||||||
|
next = node->next;
|
||||||
|
uc_htable_insert(&tmp_table, node, node->hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uc_htable_swap(&tmp_table, table);
|
||||||
|
uc_htable_destroy(&tmp_table);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ extern Suite *dbuf_suite(void);
|
|||||||
extern Suite *sprintb_suite(void);
|
extern Suite *sprintb_suite(void);
|
||||||
extern Suite *strv_suite(void);
|
extern Suite *strv_suite(void);
|
||||||
extern Suite *ratelimit_suite(void);
|
extern Suite *ratelimit_suite(void);
|
||||||
|
extern Suite *htable_suite(void);
|
||||||
|
|
||||||
static suite_func suites[] = {
|
static suite_func suites[] = {
|
||||||
bitvec_suite,
|
bitvec_suite,
|
||||||
@@ -44,6 +45,7 @@ static suite_func suites[] = {
|
|||||||
sprintb_suite,
|
sprintb_suite,
|
||||||
strv_suite,
|
strv_suite,
|
||||||
ratelimit_suite,
|
ratelimit_suite,
|
||||||
|
htable_suite,
|
||||||
clock_suite,
|
clock_suite,
|
||||||
threadqueue_suite
|
threadqueue_suite
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user