C言語でHashさせろ

汎用型ハッシュを書きました。
今なら糞コード付。



長いのでcodepadへ。
http://codepad.org/elk6geVc

使い方

createHashでハッシュのモトを作って、
setHash, getHashとかして、
freeHashで後始末。

特徴

オープンアドレス法で実装。
初期領域をcreateHash時に指定できます。
足りなくなってきたら自動的に領域を増やしてくれます。
(領域の75%にデータを登録された時に、領域を1.5倍に拡張)
キーや値はどんな型でも対応できます。(そのサイズを教える必要があります)
登録/取得/削除が可能です。
エラー処理は自己責任で。だいたい0を返してきたら問題が起こってます。
使い勝手は最悪です。

えー

まず、ハッシュの肝となるハッシュ関数から。
ハッシュ関数はうまい実装を考えられなかったので、Perlでの実装をそのまま写したレベルです。
オープンアドレス法では衝突で再計算が必要になるので、1加算してずらしていきます。


汎用型を扱えるようにするために使い勝手を捨ててます。
値をsetするにもgetするにも、変数を1個かませる必要があります。
最初はnodeで返そうかな、と思ったんですが、keyがconstでないので、
間違えてでも変更されるとハッシュが機能しなくなるので、それを恐れたため。
この辺はキャストとかでどうにかなるかもしれないけど、めんどくさくry
キーを指定するにも、値を渡すにも、その型のサイズもしくは領域の大きさも渡す必要があります。
キーに限っては、文字列で扱えたほうが何かと良いと思うので、
マクロなりで、strlenを渡すようにすればキーの長さを渡す必要がなくなります。


この使いにくいフォーマットに従えば、キーも値も様々な型に対応できます。
しかし欠点があり、キーが意図しないうちにかぶる可能性があります。
例えばキーとしての、int型整数0x34333231と、char文字列"1234"は、同じになる環境があるかもしれません。
上の文字列では、char s[4]={0x31,0x32,0x33,0x34}; になり、
上の整数をリトルエンディアンで扱う場合に、かぶることになります。



速度としても早くないです。
無駄な部分がまだまだありそうですが、ちょっといじるのがめんどくさくry
8文字のよくある文字を扱ったランダムなキーで100万のデータを、
十分な領域(再確保しない程度の領域)へset/getするのに2秒ぐらいかかります。
調べると、同条件で0.8秒台の実行速度をもつハッシュもあるようなので、3倍近く遅いことになります。
以下そのベンチマークのコードです。(上のコードのmain関数部分を入れ替えれば動きます)


そんなこんなで使いにくいことこの上ないんです。僕もこんなハッシュ使いたくないです。
ただC++にすると、引数やoperatorのオーバーロード、テンプレートにより上の問題は全部解決できます。
速度も多分テンプレートになればマシになるはず。

int main(){
	char randchar[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
	int randchar_num = sizeof(randchar)-1;
	clock_t start, end;
	int i;
//	double sum=0;
	srand(time(NULL));
	
		getchar();
	for(i=1; i<=10; ++i){
		int size = i * 100000;
		int j;
		int recv;
		HashNode *node = (HashNode *)malloc(sizeof(HashNode) * size);
		Hash *hash;
		
		// create randkey=>val pare
		for(j=0; j<size; ++j){
			int randkey_size = 8;
			int k;
			char *randkey = (char *)malloc(randkey_size + 1);
			int *randval = (int *)malloc(sizeof(int));
			*randval = j;
			for(k=0; k<randkey_size; ++k) randkey[k] = randchar[rand()%randchar_num];
			randkey[randkey_size] = '\0';
			node[j].key = randkey;
			node[j].keysize = randkey_size;
			node[j].val = randval;
			node[j].valsize = sizeof(int);
		}
		// benchmark!!
		start=clock();
		hash = createHash(3000000);
		for(j=0; j<size; ++j){
			setHash(hash, node[j].key, node[j].keysize, node[j].val, node[j].valsize);
			getHash(hash, node[j].key, node[j].keysize, &recv);
//			sum += recv;
		}
		freeHash(hash);
		end=clock();
		printf("MyHash => %5d clock [n=%d]\n", end-start, size);
		
		// free randkey=>val
		for(j=0; j<size; ++j){
			freeHashNode(node+j);
		}
		free(node);
	}
//	printf("sum: %f\n", sum);
	return 0;
}