/* ============================================================================= * CDL (Configuration Definition Language) validator $Revision: 1.4 $ * (C)2004-2007 Nikolay Nosov. All rights reserved. * * File: $RCSfile: hash_multimap.c,v $ * Purpose: Hash Multimap implementation * Written by: Nikolay Nosov * Last modified: $Date: 2008/06/21 12:15:53 $ by $Author: nnosov $. * * For more information please visit * http://nsoft.volgocity.ru/cdl or * http://cdl.sourceforge.net * ===========================================================================*/ #include #include #include #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 void *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)((const char*)key)[i]) << (j * 8); if(j == 3 || j == (size - 1)) { cs ^= ps; ps = 0; j = 0; } } } return cs; } static int default_compare_fn(const void *key1, const void *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 = (struct list_head *)malloc(sizeof(struct list_head) * map->hash_size); if(map->bucket) { for(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, const 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; }