Files
pstrace/framework/hash_multimap.cpp
T
2020-02-02 12:15:18 +04:00

317 lines
8.3 KiB
C++

/* =============================================================================
* CDL (Configuration Definition Language) validator $Revision: 1.4 $
* (C)2004-2007 Nikolai Nosov. All rights reserved.
*
* File: $RCSfile: hash_multimap.c,v $
* Purpose: Hash Multimap implementation
* Written by: Nikolai Nosov nnosov@gmail.com
* Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $.
*
* For more information please visit
* http://cdl.sourceforge.net
* ===========================================================================*/
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "hash_multimap.h"
/* Default hash function
@param key hash key
@param size hash key size in bytes
@return hash value, depends of the hash key
*/
static unsigned int default_hash_fn(const char *key, int size)
{
register int i, j;
unsigned int cs = 0;
unsigned int ps = 0;
if(key) {
for(i = 0, j = 0; i < size; i++, j++) {
ps |= ((unsigned int)key[i]) << (j * 8);
if(j == 3 || j == (size - 1)) {
cs ^= ps;
ps = 0;
j = 0;
}
}
}
return cs;
}
static int default_compare_fn(const char *key1, const char *key2, const int size)
{
return !memcmp(key1, key2, size);
}
/** Initialize hash node. Must be called before first usage of the node.
@param node pointer to the hash node descriptor
*/
void hash_node_init(struct hash_node *node)
{
node->key = NULL;
node->key_size = 0;
list_node_init(&node->node);
}
/** Cleanup hash node
@param node pointer to the hash node descriptor
*/
void hash_node_cleanup(struct hash_node *node)
{
if(node->key) free(node->key);
list_node_init(&node->node);
node->key = NULL;
node->key_size = 0;
}
/** Initialize hash table
@param map pointer to the hash table descriptor
@param hash_shift size of the hash table in bits. Should be greater than
HASH_MIN_SHIFT and less than HASH_MAX_SHIFT. In case of less than HASH_MIN_SHIFT,
will be followed by HASH_MIN_SHIFT, in case of greater HASH_MAX_SHIFT will be
followed by HASH_MAX_SHIFT
@param hf pointer to the user defined hash function. If NULL, then default hash
function will be used
@param cf pointer to the user defined compare function. if NULL, then the default
compare function will be used
*/
int hash_head_init(struct hash_head *map, unsigned int hash_shift, _hash_fn hf, _compare_fn cf)
{
int i;
if(map) {
if(hash_shift <= HASH_MIN_SHIFT) {
map->hash_shift = HASH_MIN_SHIFT;
} else if(hash_shift >= HASH_MAX_SHIFT) {
map->hash_shift = HASH_MAX_SHIFT;
} else {
map->hash_shift = hash_shift;
}
map->hash_size = 1UL << map->hash_shift;
map->hash_mask = (map->hash_size - 1);
map->bucket = (list_head*)malloc(sizeof(list_head) * map->hash_size);
if(map->bucket) {
for(uint16_t i = 0; i < map->hash_size; i++) {
list_head_init(map->bucket + i);
}
if(hf) {
map->hash_fn = hf;
} else {
map->hash_fn = default_hash_fn;
}
if(cf) {
map->compare_fn = cf;
} else {
map->compare_fn = default_compare_fn;
}
} else {
return ENOMEM;
}
} else {
return ENODEV;
}
return 0;
}
/** Cleanup hash table descriptor
@param map pointer to the hash table descriptor
*/
void hash_head_cleanup(struct hash_head *map)
{
struct list_node *p, *n;
struct hash_node *node;
int i;
if(map) {
if(map->bucket) {
for(i = 0; i < map->hash_size; i++) {
list_for_each_safe(p, n, map->bucket + i) {
node = list_entry(p, struct hash_node, node);
hash_node_cleanup(node);
}
}
free(map->bucket);
}
}
}
/** Find first node which key is equal to the key represented
@param map pointer to the hash table descriptor
@param key pointer to the hash key
@param key_size size of the hash key
@return pointer to the key found, or NULL in case of hash table have not nodes
with the key equals to the key represented
*/
struct hash_node* hash_find(struct hash_head *map, const void *key, int key_size)
{
struct list_head *list;
struct list_node *n;
struct hash_node *node;
list = map->bucket + (map->hash_fn(key, key_size) & map->hash_mask);
list_for_each(n, list) {
node = list_entry(n, struct hash_node, node);
if(key_size == node->key_size) {
if(map->compare_fn(key, node->key, key_size)) {
return node;
}
}
}
return NULL;
}
/** Find next node, which key is equal to the key represented
@param hn1 pointer to the previous node found.
@return pointer to the next node with the same key, or NULL in case of the current
node is the last node with appropriate key
*/
struct hash_node* hash_find_next(struct hash_head *map, struct hash_node *hn1)
{
struct list_node *n = &hn1->node;
struct hash_node *hn2;
while((n = list_next(n)) != NULL) {
hn2 = list_entry(n, struct hash_node, node);
if(hn1->key_size == hn2->key_size) {
if(map->compare_fn(hn1->key, hn2->key, hn2->key_size)) return hn2;
}
}
return NULL;
}
/** Add node to the hash table
@param map pointer to the hash table
@param node pointer to the node, which will be added to the table
@param key pointer to the hash key
@param key_size size of the hash key
@return zero in case of success, ENOMEM in case of error
*/
int hash_add(struct hash_head *map, struct hash_node *node, void *key, int key_size)
{
node->key = (char*)malloc(key_size);
if(node->key) {
memcpy(node->key, key, key_size);
node->key_size = key_size;
list_add_bottom(map->bucket + (map->hash_fn(key, key_size) & map->hash_mask), &node->node);
return 0;
}
return ENOMEM;
}
/** Remove node from the hash table
@param node pointer to the node descriptor
*/
void hash_del(struct hash_node *node)
{
list_del_init(&node->node);
hash_node_cleanup(node);
}
/** Initialize iterator for usage. Must be called before first iterator usage.
@param map pointer to the hash table descriptor
@param iter pointer to the iterator descriptor
*/
void hash_iterator_init(struct hash_head *map, struct hash_iterator *iter)
{
iter->map = map;
iter->map_idx = 0;
iter->current = NULL;
}
/** Move iterator to the first node in the table, and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the first node in the table, or NULL in case of error
*/
struct hash_node * hash_node_first(struct hash_iterator *iter)
{
iter->current = list_first(iter->map->bucket);
iter->map_idx = 0;
if (iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
return NULL;
}
/** Move iterator to the next node in the table and return pointer to the node
@param iter pointer to the iterator descriptor
@return pointer to the next node in the table, or NULL in case of error
*/
struct hash_node * hash_node_next(struct hash_iterator *iter)
{
if(iter->map_idx >= iter->map->hash_size) {
return NULL;
}
if(iter->current) {
iter->current = list_next(iter->current);
}
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
while(iter->map_idx < iter->map->hash_size) {
iter->current = list_first(iter->map->bucket + iter->map_idx);
iter->map_idx++;
if(iter->current) {
return list_entry(iter->current, struct hash_node, node);
}
}
return NULL;
}
/** Remove current node, pointed by iterator, and move iterator to the next node
@param iter pointer to the iterator descriptor
*/
void hash_node_del(struct hash_iterator *iter)
{
struct list_node *tmp;
tmp = iter->current;
hash_node_next(iter);
list_del_init(tmp);
}
/** Return total count of elements in the hash table
@param head pointer to the hash table descriptor
*/
int hash_count(struct hash_head *head)
{
int i;
int count = 0;
for(i = 0; i < head->hash_size; i++) {
count += list_count(head->bucket + i);
}
return count;
}