沉淀之log4c的hash
上回书说到,log4c的list模块,咱们书接上回,说一说他的hash模块,仍然用以模块代码进行注释的形式进行。
![]()
头文件如下:
hash.h:
/* $Id: hash.h,v 1.4 2005/05/24 15:33:18 legoater Exp $** Copyright 2001-2003, Meiosys (www.meiosys.com). All rights reserved.* Author: Marc Vertes <mvertes@meiosys.com>* See the COPYING file for the terms of usage and distribution.*/#ifndef __sd_hash_h#define __sd_hash_h/*** @file hash.h** @brief Generic hash table. It is implemented as an array of doubly-linked* lists of iterators. The index within the array is computed by a efficient* hash function.*/#include <stddef.h>#include <sd/defs.h>__SD_BEGIN_DECLSstruct __sd_hash_ops {unsigned int (*hash) (const void*);int (*compare) (const void*, const void*);void* (*key_dup) (const void*);void (*key_free) (void*);void* (*data_dup) (const void*);void (*data_free) (void*);};/*** 这个结构体保存hash表的操作信息*/typedef struct __sd_hash_ops sd_hash_ops_t;/*** hash表的结构体类型*/typedef struct __sd_hash sd_hash_t;struct __sd_hash_iter {void* key;void* data;struct __sd_hash* hash;unsigned int __hkey;struct __sd_hash_iter* __next;struct __sd_hash_iter* __prev;int __foreach;};/*** hash表元素的容器,用来存放用户数据*/typedef struct __sd_hash_iter sd_hash_iter_t;/*** 遍历的回调函数类型*/typedef unsigned int (*sd_hash_func_t)(void* a_key, void* a_data,void* a_userdata);/*** 新建一个hash表,用户可以指定内存的分配方式以及key值计算和数据存储方式* @param a_size 初始化数组的大小* @param a_ops hash操作函数,如果是NULL,默认字符串key值算法,另外没有为* key值计算和用户数据存储方式* @return hash表结构*/extern sd_hash_t* sd_hash_new(size_t a_size, const sd_hash_ops_t* a_ops);/*** 销毁一个hash表*/extern void sd_hash_delete(sd_hash_t* a_this);/*** 清空一个hash表*/extern void sd_hash_clear(sd_hash_t* a_this);/*** 根据key值在hash表里查找元素容器* @param a_key key值* @return 指向找到的结构体地址或者NULL*/extern sd_hash_iter_t* sd_hash_lookup(sd_hash_t* a_this, const void* a_key);/*** 根据key值在hash表里查找元素容器,如果不存在就新建一个。* @param a_key 与待查找容器相关联的key值* @return 返回查找到的容器或者NULL.*/extern sd_hash_iter_t* sd_hash_lookadd(sd_hash_t* a_this, const void* a_key);/*** 通过给定的数据key值添加用户数据到hash表,如果已经存在那么就通过hash操作中的* 释放接口进行释放之前的,并将当前数据插入* @param a_key 容器的键值key值* @param a_data 数据* @return 返回指向数据容器的指针.*/extern sd_hash_iter_t* sd_hash_add(sd_hash_t* a_this, const void* a_key,void* a_data);/*** 从hash表中移除一个容器* @param 容器的key值*/extern void sd_hash_del(sd_hash_t* a_this, const void* a_key);/*** 调用回调函数a_fun遍历整个hash,直到该回调返回非0* @param a_func 回调函数* @param a_data 用户数据传如到a_func*/extern void sd_hash_foreach(sd_hash_t* a_this, sd_hash_func_t a_func,void* a_data);/*** 获得hash表中所有的容器个数*/extern unsigned int sd_hash_get_nelem(sd_hash_t* a_this);/*** 获得hash表的大小*/extern unsigned int sd_hash_get_size(sd_hash_t* a_this);/*** 获取hash表的第一个元素容器*/extern sd_hash_iter_t* sd_hash_begin(sd_hash_t* a_this);/*** 获取hash表的最后一个元素容器*/extern sd_hash_iter_t* sd_hash_end(sd_hash_t* a_this);/*** 获取当前容器的下一个容器*/extern sd_hash_iter_t* sd_hash_iter_next(sd_hash_iter_t* a_this);/*** 获取当前容器的上一个容器*/extern sd_hash_iter_t* sd_hash_iter_prev(sd_hash_iter_t* a_this);/*** 删除一个容器,根据容器地址*/extern void sd_hash_iter_del(sd_hash_iter_t* a_this);/*** Hashes strings.计算key值*/extern unsigned int sd_hash_hash_string(const char* a_string);__SD_END_DECLS#endif
头文件里面定义了hash操作的结构体,hash表元素容器的结构体,以及hash表的类型,其中hash表的类型是在hash.c中进行定义的,这样很好的屏蔽了hash结构。
对于hash表的结构体定义如下:
#define SD_HASH_FULLTAB 2 /* rehash when table gets this x full */#define SD_HASH_GROWTAB 4 /* grow table by this factor */#define SD_HASH_DEFAULT_SIZE 10 /* self explenatory */struct __sd_hash {size_t nelem; //hash表中元素个数size_t size; //hash大小sd_hash_iter_t** tab; //元素容器的表const sd_hash_ops_t* ops; //hash操作};#define hindex(h, n) ((h)%(n)) //计算hashkey
通过以上各个结构体的定义可以先给出整个hash表的总体结构,然后后续参考sd_hash_new进一步进行理解。

图一:hash结构图
具体代码以及分析如下:
sd_hash_new:
/******************************************************************************///创建一个hash表//a_size hash表能够容纳元素的数量//a_ops hash操作集合extern sd_hash_t* sd_hash_new(size_t a_size, const sd_hash_ops_t* a_ops){//一个默认的hash操作集合、hash值计算,比较等const static sd_hash_ops_t default_ops = {(void*) &sd_hash_hash_string,(void*) &strcmp,0, 0, 0, 0};//分配hash表和容器表内存并进行检查sd_hash_t* hash;sd_hash_iter_t** tab;if (a_size == 0) a_size = SD_HASH_DEFAULT_SIZE;hash = sd_calloc(1, sizeof(*hash));tab = sd_calloc(a_size, sizeof(*tab));if (hash == 0 || tab == 0) {free(hash);free(tab);return 0;}//初始化元素个数,大小,容器表地址以及操作集合hash->nelem = 0;hash->size = a_size;hash->tab = tab;hash->ops = a_ops != 0 ? a_ops : &default_ops;return hash;}
sd_hash_delete:
/******************************************************************************///销毁一个hash表,先将hash表清空,然后将hash进行回收//a_this 待清空的hash表extern void sd_hash_delete(sd_hash_t* a_this){//将hash表中的元素进行清空sd_hash_clear(a_this);//释放hash的容器表和hash表free(a_this->tab);free(a_this);}
sd_hash_clear:
/******************************************************************************///清空一个hash表中的元素//a_this 待清空的hash表extern void sd_hash_clear(sd_hash_t* a_this){size_t h;sd_hash_iter_t* p;sd_hash_iter_t* q;if (a_this == 0) return;//遍历一个hash的所有容器指针的下挂的所有容器,进行释放for (h = 0; h < a_this->size; h++) {for (p = a_this->tab[h]; p; p = q) {q = p->__next;if (a_this->ops->key_free) a_this->ops->key_free(p->key);if (a_this->ops->data_free) a_this->ops->data_free(p->data);free(p);}a_this->tab[h] = 0;}a_this->nelem = 0;}
sd_hash_lookup:
/*** 根据key值在hash表里查找元素容器* @param a_key key值* @return 指向找到的结构体地址或者NULL*/extern sd_hash_iter_t* sd_hash_lookup(sd_hash_t* a_this, const void* a_key){int h;sd_hash_iter_t* p;//参数判断if (a_this == 0 || a_key == 0) return 0;//计算hash值h = hindex(a_this->ops->hash(a_key), a_this->size);//遍历该hash值所对应的容器指针下挂的所有容器并进行比较验证for (p = a_this->tab[h]; p != 0; p = p->__next)if (a_this->ops->compare(a_key, p->key) == 0) {return p;}return 0;}
sd_hash_lookadd:
/*** 根据key值在hash表里查找元素容器,如果不存在就新建一个。* @param a_key 与待查找容器相关联的key值* @return 返回查找到的容器或者NULL.*/extern sd_hash_iter_t* sd_hash_lookadd(sd_hash_t* a_this, const void* a_key){int h;sd_hash_iter_t* p;if (a_this == 0 || a_key == 0) return 0;//先查找一遍,已经存在就进行返回,否则就分配空间然后进行初始化if ((p = sd_hash_lookup(a_this, a_key)) != 0) return p;if ((p = sd_calloc(1, sizeof(*p))) == 0) return 0;//如果有复制数据功能那么就复制一份否则就用原始数据if (a_this->ops->key_dup != 0)p->key = a_this->ops->key_dup(a_key);elsep->key = (void*) a_key;//初始化容器一些参数p->hash = a_this;p->__hkey = a_this->ops->hash(a_key);//如果超过当前hash最大容量那么就进行扩张if (a_this->nelem > SD_HASH_FULLTAB * a_this->size) rehash(a_this);h = hindex(p->__hkey,a_this->size);//将当前容器挂到对应key值的容器指针下挂链表中。p->__next = a_this->tab[h];a_this->tab[h] = p;if (p->__next != 0) p->__next->__prev = p;//计数a_this->nelem++;return p;}
sd_hash_add:
/*** 通过给定的数据key值添加用户数据到hash表,如果已经存在那么就通过hash操作中的* 释放接口进行释放之前的,并将当前数据插入* @param a_key 容器的键值key值* @param a_data 数据* @return 返回指向数据容器的指针.*/extern sd_hash_iter_t* sd_hash_add(sd_hash_t* a_this, const void* a_key,void* a_data){sd_hash_iter_t* p;//调用lookadd进行插入if ((p = sd_hash_lookadd(a_this, a_key)) == 0) return 0;//如果这个值已经存在,那么就释放了然后重新进行赋值当前值if (a_this->ops->data_free != 0) a_this->ops->data_free(p->data);//判断释放需要进行备份并进行赋值if (a_this->ops->data_dup != 0)p->data = a_this->ops->data_dup(a_data);elsep->data = a_data;return p;}
sd_hash_del:
/*** 从hash表中移除一个容器* @param 容器的key值*/extern void sd_hash_del(sd_hash_t* a_this, const void* a_key){int h;sd_hash_iter_t* p;//定位容器的容器指针h = hindex(a_this->ops->hash(a_key), a_this->size);//循环遍历容器指针下挂的链表找到待删除容器进行删除for (p = a_this->tab[h]; p != 0; p = p->__next)if (a_this->ops->compare(a_key, p->key) == 0) break;if (p == 0) return;//进行删除sd_hash_iter_del(p);}
sd_hash_foreach:
/*** 调用回调函数a_fun遍历整个hash,直到该回调返回非0* @param a_func 回调函数* @param a_data 用户数据传如到a_func*/extern void sd_hash_foreach(sd_hash_t* a_this, sd_hash_func_t a_func,void* a_data){size_t h, ret;sd_hash_iter_t* p;sd_hash_iter_t* q;//参数验证if (a_this == 0 || a_func == 0) return;//定位容器指针,找到容器for (h = 0; h < a_this->size; h++)for (p = a_this->tab[h]; p != 0; p = q) {p->__foreach = 1;//调用遍历回调函数进行操作ret = (*a_func)(p->key, p->data, a_data);q = p->__next;//删除重复表项if (p->__foreach == 0)sd_hash_iter_del(p);elsep->__foreach = 0;if (ret != 0) return;}}
sd_hash_get_nelem:
/*** 获得hash表中所有的容器个数*/extern unsigned int sd_hash_get_nelem(sd_hash_t* a_this){if (a_this == 0) return 0;return a_this->nelem;}
sd_hash_get_size:
/*** 获得hash表的大小*/extern unsigned int sd_hash_get_size(sd_hash_t* a_this){if (a_this == 0) return 0;return a_this->size;}
sd_hash_begin:
/*** 获取hash表的第一个元素容器*/extern sd_hash_iter_t* sd_hash_begin(sd_hash_t* a_this){size_t h;if (a_this == 0) return 0;for (h = 0; h < a_this->size; h++)if (a_this->tab[h])return a_this->tab[h];return 0;}
sd_hash_end:未实现
/*** 获取hash表的最后一个元素容器*/extern sd_hash_iter_t* sd_hash_end(sd_hash_t* a_this){return 0;}
sd_hash_iter_next:
/*** 获取当前容器的下一个容器*/extern sd_hash_iter_t* sd_hash_iter_next(sd_hash_iter_t* a_this){int h;size_t i;//参数检查并进行链表递进后返回if (a_this == 0) return 0;if (a_this->__next != 0) return a_this->__next;//如果当前容器就是其容器指针的最后一个,那么要寻找到下一个容器指针的//第一个容器进行返回h = hindex(a_this->__hkey, a_this->hash->size);for (i = h + 1; i < a_this->hash->size; i++)if (a_this->hash->tab[i])return a_this->hash->tab[i];return 0;}
sd_hash_iter_prev:
/*** 获取当前容器的上一个容器*/extern sd_hash_iter_t* sd_hash_iter_prev(sd_hash_iter_t* a_this){int h, i;sd_hash_iter_t* p;//参数验证,然后如果在当前容器指针的链表下存在前一个指针那么进行返回if (a_this == 0) return 0;if (a_this->__prev != 0) return a_this->__prev;//如果当前容器就是该链表下的第一个,那么返回前一个容器指针的第一个有效容器h = hindex(a_this->__hkey, a_this->hash->size);for (i = h - 1; i > 0; i--)for (p = a_this->hash->tab[i]; p; p = p->__next)if (p->__next == 0)return p;return 0;}
sd_hash_iter_del:
/*** 删除一个容器,根据容器地址*/extern void sd_hash_iter_del(sd_hash_iter_t* a_this){if (a_this == 0) return;//释放容器存放的数据if (a_this->hash->ops->data_free != 0)a_this->hash->ops->data_free(a_this->data);a_this->data = 0;//释放容器的key值内存if (a_this->hash->ops->key_free != 0)a_this->hash->ops->key_free(a_this->key);a_this->key = 0;//已经删除的即不再进行遍历了。if (a_this->__foreach == 1) {a_this->__foreach = 0;return;}//从链表中摘除if (a_this->__next != 0) a_this->__next->__prev = a_this->__prev;if (a_this->__prev != 0)a_this->__prev->__next = a_this->__next;elsea_this->hash->tab[hindex(a_this->__hkey, a_this->hash->size)] =a_this->__next;//减一数目a_this->hash->nelem--;//释放free(a_this);}
sd_hash_hash_string:
/*** Hashes strings.计算key值*/extern unsigned int sd_hash_hash_string(const char* a_string){register unsigned int h;//一个hash计算方法for (h = 0; *a_string != '\0'; a_string++)h = *a_string + 31 * h;return h;}
一个示例程序
main.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "stack.h"#include "hash.h"#include "list.h"typedef struct test{char a[20];int b;int c;}test_t;static unsigned int print(void *data, void *p){test_t *t = (test_t *)data;printf("a:%s b:%d c:%d\n",t->a, t->b, t->c);return 0;}unsigned int print_hash(void *a_key, void *a_data, void *a_userdata){print(a_data, NULL);return 0;}int main(){sd_hash_ops_t ops = {(void*) &sd_hash_hash_string,(void*) &strcmp,0, 0, 0, free};sd_hash_t*hash = sd_hash_new(20, &ops);test_t *fir = malloc(sizeof(test_t));strcpy(fir->a,"zhang");fir->b = 10;fir->c = 20;sd_hash_add(hash, fir->a,fir);test_t *sec = malloc(sizeof(test_t));strcpy(sec->a,"wang");sec->b = 20;sec->c = 30;sd_hash_add(hash, sec->a,sec);test_t *third = malloc(sizeof(test_t));strcpy(third->a,"zhongguo");third->b = 30;third->c = 40;sd_hash_iter_t *iter = sd_hash_lookadd(hash, "zhongguo");iter->data = third;int cnt = sd_hash_get_nelem(hash);printf("hash items is : %d\n",cnt);sd_hash_foreach(hash, print_hash, NULL);sd_hash_clear(hash);sd_hash_delete(hash);return 0;}
浙公网安备 33010602011771号