added open-addressed hash table
This commit is contained in:
		| @ -7,7 +7,7 @@ libqsecmn_la_SOURCES = \ | ||||
| 	syscall.h mem.h \ | ||||
| 	mem.c xma.c fma.c chr.c chr_cnv.c rex.c \ | ||||
| 	str_bas.c str_cnv.c str_dyn.c str_utl.c \ | ||||
| 	lda.c htb.c rbt.c sll.c gdl.c dll.c opt.c \ | ||||
| 	lda.c oht.c htb.c rbt.c sll.c gdl.c dll.c opt.c \ | ||||
| 	tio.c tio_get.c tio_put.c \ | ||||
| 	fio.c pio.c sio.c \ | ||||
| 	time.c \ | ||||
|  | ||||
| @ -75,9 +75,9 @@ LTLIBRARIES = $(lib_LTLIBRARIES) | ||||
| libqsecmn_la_DEPENDENCIES = | ||||
| am_libqsecmn_la_OBJECTS = mem.lo xma.lo fma.lo chr.lo chr_cnv.lo \ | ||||
| 	rex.lo str_bas.lo str_cnv.lo str_dyn.lo str_utl.lo lda.lo \ | ||||
| 	htb.lo rbt.lo sll.lo gdl.lo dll.lo opt.lo tio.lo tio_get.lo \ | ||||
| 	tio_put.lo fio.lo pio.lo sio.lo time.lo misc.lo assert.lo \ | ||||
| 	main.lo stdio.lo | ||||
| 	oht.lo htb.lo rbt.lo sll.lo gdl.lo dll.lo opt.lo tio.lo \ | ||||
| 	tio_get.lo tio_put.lo fio.lo pio.lo sio.lo time.lo misc.lo \ | ||||
| 	assert.lo main.lo stdio.lo | ||||
| libqsecmn_la_OBJECTS = $(am_libqsecmn_la_OBJECTS) | ||||
| libqsecmn_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ | ||||
| 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ | ||||
| @ -264,7 +264,7 @@ libqsecmn_la_SOURCES = \ | ||||
| 	syscall.h mem.h \ | ||||
| 	mem.c xma.c fma.c chr.c chr_cnv.c rex.c \ | ||||
| 	str_bas.c str_cnv.c str_dyn.c str_utl.c \ | ||||
| 	lda.c htb.c rbt.c sll.c gdl.c dll.c opt.c \ | ||||
| 	lda.c oht.c htb.c rbt.c sll.c gdl.c dll.c opt.c \ | ||||
| 	tio.c tio_get.c tio_put.c \ | ||||
| 	fio.c pio.c sio.c \ | ||||
| 	time.c \ | ||||
| @ -369,6 +369,7 @@ distclean-compile: | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oht.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opt.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pio.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rbt.Plo@am__quote@ | ||||
|  | ||||
							
								
								
									
										322
									
								
								qse/lib/cmn/oht.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								qse/lib/cmn/oht.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,322 @@ | ||||
| #include <qse/cmn/oht.h> | ||||
| #include "mem.h" | ||||
|  | ||||
| #define DATA_PTR(oht,index) \ | ||||
| 	((void*)(((qse_byte_t*)(oht)->data) + ((index) * (oht)->scale))) | ||||
|  | ||||
| QSE_IMPLEMENT_COMMON_FUNCTIONS (oht) | ||||
|  | ||||
| static QSE_INLINE_ALWAYS qse_size_t default_hasher ( | ||||
| 	qse_oht_t* oht, const void* data) | ||||
| { | ||||
| 	size_t h = 5381; | ||||
| 	const qse_byte_t* p = (const qse_byte_t*)data; | ||||
| 	const qse_byte_t* bound = p + oht->scale; | ||||
| 	while (p < bound) h = ((h << 5) + h) + *p++; | ||||
| 	return h ;  | ||||
| } | ||||
|  | ||||
| static QSE_INLINE_ALWAYS int default_comper ( | ||||
| 	qse_oht_t* oht, const void* data1, const void* data2) | ||||
| { | ||||
| 	return QSE_MEMCMP(data1, data2, oht->scale); | ||||
| } | ||||
|  | ||||
| static QSE_INLINE_ALWAYS void default_copier ( | ||||
| 	qse_oht_t* oht, void* dst, const void* src) | ||||
| { | ||||
| 	QSE_MEMCPY (dst, src, oht->scale); | ||||
| } | ||||
|  | ||||
| #define HASH_DATA(oht,data) \ | ||||
| 		((oht)->hasher? (oht)->hasher ((oht), (data)): \ | ||||
| 		                default_hasher (oht, data)) | ||||
|  | ||||
| #define COMP_DATA(oht,d1,d2) \ | ||||
| 		((oht)->comper? (oht)->comper ((oht), (d1), (d2)): \ | ||||
| 		                QSE_MEMCMP ((d1), (d2), (oht)->scale)) | ||||
|  | ||||
| #define COPY_DATA(oht,dst,src) \ | ||||
| 	QSE_BLOCK ( \ | ||||
| 		if ((oht)->copier) (oht)->copier ((oht), (dst), (src)); \ | ||||
| 		else QSE_MEMCPY ((dst), (src), (oht)->scale); \ | ||||
| 	)  | ||||
|  | ||||
| qse_oht_t* qse_oht_open ( | ||||
| 	qse_mmgr_t* mmgr, qse_size_t xtnsize, | ||||
| 	qse_size_t scale, qse_size_t capa, qse_size_t limit)  | ||||
| { | ||||
| 	qse_oht_t* oht; | ||||
|  | ||||
| 	if (mmgr == QSE_NULL)  | ||||
| 	{ | ||||
| 		mmgr = QSE_MMGR_GETDFL(); | ||||
|  | ||||
| 		QSE_ASSERTX (mmgr != QSE_NULL, | ||||
| 			"Set the memory manager with QSE_MMGR_SETDFL()"); | ||||
|  | ||||
| 		if (mmgr == QSE_NULL) return QSE_NULL; | ||||
| 	} | ||||
|  | ||||
| 	oht = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_oht_t) + xtnsize); | ||||
| 	if (oht == QSE_NULL) return QSE_NULL; | ||||
|  | ||||
| 	if (qse_oht_init (oht, mmgr, scale, capa, limit) == QSE_NULL) | ||||
| 	{ | ||||
| 		QSE_MMGR_FREE (mmgr, oht); | ||||
| 		return QSE_NULL; | ||||
| 	} | ||||
|  | ||||
| 	return oht; | ||||
| } | ||||
|  | ||||
| void qse_oht_close (qse_oht_t* oht) | ||||
| { | ||||
| 	qse_oht_fini (oht); | ||||
| 	QSE_MMGR_FREE (oht->mmgr, oht); | ||||
| } | ||||
|  | ||||
| qse_oht_t* qse_oht_init ( | ||||
| 	qse_oht_t* oht, qse_mmgr_t* mmgr, | ||||
| 	qse_size_t scale, qse_size_t capa, qse_size_t limit)  | ||||
| { | ||||
| 	qse_size_t i; | ||||
|  | ||||
| 	if (scale <= 0) scale = 1; | ||||
| 	if (capa >= QSE_OHT_INVALID_INDEX - 1) capa = QSE_OHT_INVALID_INDEX - 1; | ||||
| 	if (limit > capa || limit <= 0) limit = capa; | ||||
|  | ||||
| 	QSE_MEMSET (oht, 0, QSE_SIZEOF(*oht)); | ||||
|  | ||||
| 	oht->mmgr = mmgr; | ||||
| 	oht->capa.hard = capa; | ||||
| 	oht->capa.soft = limit; | ||||
| 	oht->scale = scale; | ||||
| 	oht->size = 0; | ||||
|  | ||||
| 	/*oht->hasher = default_hasher; | ||||
| 	oht->comper = default_comper; | ||||
| 	oht->copier = default_copier;*/ | ||||
|  | ||||
| 	oht->mark = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_oht_mark_t) * capa); | ||||
| 	if (!oht->mark) return QSE_NULL; | ||||
|  | ||||
| 	oht->data = QSE_MMGR_ALLOC (mmgr, scale * capa); | ||||
| 	if (!oht->data) | ||||
| 	{ | ||||
| 		QSE_MMGR_FREE (mmgr, oht->mark); | ||||
| 		return QSE_NULL; | ||||
| 	} | ||||
|  | ||||
| 	for (i = 0; i < capa; i++) oht->mark[i] = QSE_OHT_EMPTY; | ||||
| 	return oht; | ||||
| } | ||||
|  | ||||
| void qse_oht_fini (qse_oht_t* oht) | ||||
| { | ||||
| 	QSE_MMGR_FREE (oht->mmgr, oht->mark); | ||||
| 	QSE_MMGR_FREE (oht->mmgr, oht->data); | ||||
| 	oht->size = 0; | ||||
| } | ||||
|  | ||||
| qse_oht_hasher_t qse_oht_gethasher (qse_oht_t* oht) | ||||
| { | ||||
| 	return oht->hasher? oht->hasher: default_hasher; | ||||
| } | ||||
|  | ||||
| void qse_oht_sethasher (qse_oht_t* oht, qse_oht_hasher_t hasher) | ||||
| { | ||||
| 	oht->hasher = hasher; | ||||
| } | ||||
|  | ||||
| qse_oht_comper_t qse_oht_getcomper (qse_oht_t* oht) | ||||
| { | ||||
| 	return oht->comper? oht->comper: default_comper; | ||||
| } | ||||
|  | ||||
| void qse_oht_setcomper (qse_oht_t* oht, qse_oht_comper_t comper) | ||||
| { | ||||
| 	oht->comper = comper; | ||||
| } | ||||
|  | ||||
| qse_oht_copier_t qse_oht_getcopier (qse_oht_t* oht) | ||||
| { | ||||
| 	return oht->copier? oht->copier: default_copier; | ||||
| } | ||||
|  | ||||
| void qse_oht_setcopier (qse_oht_t* oht, qse_oht_copier_t copier) | ||||
| { | ||||
| 	oht->copier = copier; | ||||
| } | ||||
|  | ||||
| static QSE_INLINE qse_size_t search ( | ||||
| 	qse_oht_t* oht, const void* data, qse_size_t hash) | ||||
| { | ||||
| 	qse_size_t i; | ||||
|  | ||||
| 	for (i = 0; i < oht->capa.hard; i++) | ||||
| 	{ | ||||
| 		qse_size_t index = (hash + i) % oht->capa.hard; | ||||
| 		if (oht->mark[index] == QSE_OHT_EMPTY) break; | ||||
| 		if (oht->mark[index] == QSE_OHT_OCCUPIED) | ||||
| 		{ | ||||
| 			if (COMP_DATA (oht, data, DATA_PTR(oht,index)) == 0)  | ||||
| 				return index; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return QSE_OHT_INVALID_INDEX; | ||||
| } | ||||
|  | ||||
| qse_size_t qse_oht_search (qse_oht_t* oht, void* data) | ||||
| { | ||||
| 	qse_size_t i = search (oht, data, HASH_DATA(oht,data)); | ||||
| 	if (i != QSE_OHT_INVALID_INDEX && data)  | ||||
| 		COPY_DATA (oht, data, DATA_PTR(oht,i)); | ||||
| 	return i; | ||||
| } | ||||
|  | ||||
| qse_size_t qse_oht_update (qse_oht_t* oht, const void* data) | ||||
| { | ||||
| 	qse_size_t i = search (oht, data, HASH_DATA(oht,data)); | ||||
| 	if (i != QSE_OHT_INVALID_INDEX)  | ||||
| 		COPY_DATA (oht, DATA_PTR(oht,i), data); | ||||
| 	return i; | ||||
| } | ||||
|  | ||||
| qse_size_t qse_oht_upsert (qse_oht_t* oht, const void* data) | ||||
| { | ||||
| 	qse_size_t i, hash = HASH_DATA (oht, data); | ||||
|  | ||||
| 	/* find the existing item */ | ||||
| 	i = search (oht, data, hash); | ||||
| 	if (i != QSE_OHT_INVALID_INDEX) | ||||
| 	{ | ||||
| 		COPY_DATA (oht, DATA_PTR(oht,i), data); | ||||
| 		return i; | ||||
| 	} | ||||
|  | ||||
| 	/* check if there is a free slot to insert data into */ | ||||
| 	if (oht->size >= oht->capa.soft) return QSE_OHT_INVALID_INDEX; | ||||
|  | ||||
| 	/* get the unoccupied slot and insert the data into it. | ||||
| 	 * iterate at most 'the number of items (oht->size)' times + 1. */ | ||||
| 	for (i = 0; i <= oht->size; i++)  | ||||
| 	{ | ||||
| 		qse_size_t index = (hash + i) % oht->capa.hard; | ||||
| 		if (oht->mark[index] != QSE_OHT_OCCUPIED)  | ||||
| 		{ | ||||
| 			oht->mark[index] = QSE_OHT_OCCUPIED; | ||||
| 			COPY_DATA (oht, DATA_PTR(oht,index), data); | ||||
| 			oht->size++; | ||||
| 			return index; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return QSE_OHT_INVALID_INDEX; | ||||
| } | ||||
|  | ||||
| qse_size_t qse_oht_insert (qse_oht_t* oht, const void* data) | ||||
| { | ||||
| 	qse_size_t i, hash; | ||||
|  | ||||
| 	/* check if there is a free slot to insert data into */ | ||||
| 	if (oht->size >= oht->capa.soft) return QSE_OHT_INVALID_INDEX; | ||||
|  | ||||
| 	hash = HASH_DATA (oht, data); | ||||
|  | ||||
| 	/* check if the item already exits */ | ||||
| 	i = search (oht, data, hash); | ||||
| 	if (i != QSE_OHT_INVALID_INDEX) return QSE_OHT_INVALID_INDEX; | ||||
|  | ||||
| 	/* get the unoccupied slot and insert the data into it. | ||||
| 	 * iterate at most 'the number of items (oht->size)' times + 1. */ | ||||
| 	for (i = 0; i <= oht->size; i++)  | ||||
| 	{ | ||||
| 		qse_size_t index = (hash + i) % oht->capa.hard; | ||||
| 		if (oht->mark[index] != QSE_OHT_OCCUPIED)  | ||||
| 		{ | ||||
| 			oht->mark[index] = QSE_OHT_OCCUPIED; | ||||
| 			COPY_DATA (oht, DATA_PTR(oht,index), data); | ||||
| 			oht->size++; | ||||
| 			return index; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return QSE_OHT_INVALID_INDEX; | ||||
| } | ||||
|  | ||||
| qse_size_t qse_oht_delete (qse_oht_t* oht, const void* data) | ||||
| { | ||||
| #if 0 | ||||
| 	qse_size_t index; | ||||
|  | ||||
| 	if (oht->size <= 0) return QSE_OHT_INVALID_INDEX;  | ||||
|  | ||||
| 	index = search (oht, data, HASH_DATA(oht,data)); | ||||
| 	if (index != QSE_OHT_INVALID_INDEX) | ||||
| 	{ | ||||
| 		oht->mark[index] = QSE_OHT_DELETED; | ||||
| 		oht->size--; | ||||
| 	} | ||||
|  | ||||
| 	return index; | ||||
| #endif | ||||
|  | ||||
| 	qse_size_t index, i, x, y, z; | ||||
|  | ||||
| 	/* check if the oht is empty. if so, do nothing */ | ||||
| 	if (oht->size <= 0) return QSE_OHT_INVALID_INDEX;  | ||||
|  | ||||
| 	/* check if the item exists. otherwise, do nothing. */ | ||||
| 	index = search (oht, data, HASH_DATA(oht,data)); | ||||
| 	if (index == QSE_OHT_INVALID_INDEX) return QSE_OHT_INVALID_INDEX; | ||||
|  | ||||
| 	/* compact the cluster */ | ||||
| 	for (i = 0, x = index, y = index; i < oht->size; i++) | ||||
| 	{ | ||||
| 		y = (y + 1) % oht->capa.hard; | ||||
|  | ||||
| 		/* done if the slot at the current hash index is empty */ | ||||
| 		if (oht->mark[y] == QSE_OHT_EMPTY) break; | ||||
|  | ||||
| 		/* get the natural hash index for the data in the slot at  | ||||
| 		 * the current hash index */ | ||||
| 		z = HASH_DATA(oht,DATA_PTR(oht,y)) % oht->capa.hard; | ||||
| 		 | ||||
| 		/* move an element if necesary */ | ||||
| 		if ((y > x && (z <= x || z > y)) || | ||||
| 		    (y < x && (z <= x && z > y))) | ||||
| 		{ | ||||
| 			COPY_DATA (oht, DATA_PTR(oht,x), DATA_PTR(oht,y)); | ||||
| 			x = y; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	oht->mark[x] = QSE_OHT_EMPTY; | ||||
| 	oht->size--; | ||||
|  | ||||
| 	return index; | ||||
| } | ||||
|  | ||||
| void qse_oht_clear (qse_oht_t* oht) | ||||
| { | ||||
| 	qse_size_t i; | ||||
| 	for (i = 0; i < oht->capa.hard; i++) | ||||
| 		oht->mark[i] = QSE_OHT_EMPTY; | ||||
| 	oht->size = 0; | ||||
| } | ||||
|  | ||||
| void qse_oht_walk (qse_oht_t* oht, qse_oht_walker_t walker, void* ctx) | ||||
| { | ||||
| 	qse_size_t i; | ||||
|  | ||||
| 	for (i = 0; i < oht->capa.hard; i++) | ||||
| 	{ | ||||
| 		if (oht->mark[i] == QSE_OHT_OCCUPIED) | ||||
| 		{ | ||||
| 			if (walker (oht, DATA_PTR(oht,i), ctx) == QSE_OHT_WALK_STOP)  | ||||
| 				return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -702,7 +702,7 @@ void qse_xma_free (qse_xma_t* xma, void* b) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void qse_xma_dump (qse_xma_t* xma, int (*printf)(const qse_char_t* fmt,...)) | ||||
| void qse_xma_dump (qse_xma_t* xma,  qse_xma_dumper_t dumper, void* target) | ||||
| { | ||||
| 	qse_xma_blk_t* tmp; | ||||
| 	unsigned long long fsum, asum;  | ||||
| @ -710,19 +710,19 @@ void qse_xma_dump (qse_xma_t* xma, int (*printf)(const qse_char_t* fmt,...)) | ||||
| 	unsigned long long isum; | ||||
| #endif | ||||
|  | ||||
| 	printf (QSE_T("<XMA DUMP>\n")); | ||||
| 	dumper (target, QSE_T("<XMA DUMP>\n")); | ||||
| #ifdef QSE_XMA_ENABLE_STAT | ||||
| 	printf (QSE_T("== statistics ==\n")); | ||||
| 	printf (QSE_T("total = %llu\n"), (unsigned long long)xma->stat.total); | ||||
| 	printf (QSE_T("alloc = %llu\n"), (unsigned long long)xma->stat.alloc); | ||||
| 	printf (QSE_T("avail = %llu\n"), (unsigned long long)xma->stat.avail); | ||||
| 	dumper (target, QSE_T("== statistics ==\n")); | ||||
| 	dumper (target, QSE_T("total = %llu\n"), (unsigned long long)xma->stat.total); | ||||
| 	dumper (target, QSE_T("alloc = %llu\n"), (unsigned long long)xma->stat.alloc); | ||||
| 	dumper (target, QSE_T("avail = %llu\n"), (unsigned long long)xma->stat.avail); | ||||
| #endif | ||||
|  | ||||
| 	printf (QSE_T("== blocks ==\n")); | ||||
| 	printf (QSE_T(" size               avail address\n")); | ||||
| 	dumper (target, QSE_T("== blocks ==\n")); | ||||
| 	dumper (target, QSE_T(" size               avail address\n")); | ||||
| 	for (tmp = xma->head, fsum = 0, asum = 0; tmp; tmp = tmp->b.next) | ||||
| 	{ | ||||
| 		printf (QSE_T(" %-18llu %-5d %p\n"), (unsigned long long)tmp->size, tmp->avail, tmp); | ||||
| 		dumper (target, QSE_T(" %-18llu %-5d %p\n"), (unsigned long long)tmp->size, tmp->avail, tmp); | ||||
| 		if (tmp->avail) fsum += tmp->size; | ||||
| 		else asum += tmp->size; | ||||
| 	} | ||||
| @ -731,12 +731,12 @@ void qse_xma_dump (qse_xma_t* xma, int (*printf)(const qse_char_t* fmt,...)) | ||||
| 	isum = (xma->stat.nfree + xma->stat.nused) * HDRSIZE; | ||||
| #endif | ||||
|  | ||||
| 	printf (QSE_T("---------------------------------------\n")); | ||||
| 	printf (QSE_T("Allocated blocks: %18llu bytes\n"), asum); | ||||
| 	printf (QSE_T("Available blocks: %18llu bytes\n"), fsum); | ||||
| 	dumper (target, QSE_T("---------------------------------------\n")); | ||||
| 	dumper (target, QSE_T("Allocated blocks: %18llu bytes\n"), asum); | ||||
| 	dumper (target, QSE_T("Available blocks: %18llu bytes\n"), fsum); | ||||
| #ifdef QSE_XMA_ENABLE_STAT | ||||
| 	printf (QSE_T("Internal use    : %18llu bytes\n"), isum); | ||||
| 	printf (QSE_T("Total           : %18llu bytes\n"), asum + fsum + isum); | ||||
| 	dumper (target, QSE_T("Internal use    : %18llu bytes\n"), isum); | ||||
| 	dumper (target, QSE_T("Total           : %18llu bytes\n"), asum + fsum + isum); | ||||
| #endif | ||||
|  | ||||
| 	QSE_ASSERT (asum == xma->stat.alloc); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user