qse/lib/cmn/oht.c

318 lines
7.0 KiB
C
Raw Normal View History

2010-09-08 04:57:43 +00:00
#include <qse/cmn/oht.h>
2019-03-05 08:21:01 +00:00
#include <qse/hash.h>
2016-04-29 03:55:42 +00:00
#include "mem-prv.h"
2010-09-08 04:57:43 +00:00
#define DATA_PTR(oht,index) \
((void*)(((qse_byte_t*)(oht)->data) + ((index) * (oht)->scale)))
2019-03-01 09:27:03 +00:00
static QSE_INLINE_ALWAYS qse_size_t default_hasher (qse_oht_t* oht, const void* data)
2010-09-08 04:57:43 +00:00
{
2019-03-05 08:21:01 +00:00
qse_size_t h;
QSE_HASH_BYTES (h, data, oht->scale);
2010-09-08 04:57:43 +00:00
return h ;
}
2019-03-01 09:27:03 +00:00
static QSE_INLINE_ALWAYS int default_comper (qse_oht_t* oht, const void* data1, const void* data2)
2010-09-08 04:57:43 +00:00
{
return QSE_MEMCMP(data1, data2, oht->scale);
}
2019-03-01 09:27:03 +00:00
static QSE_INLINE_ALWAYS void default_copier (qse_oht_t* oht, void* dst, const void* src)
2010-09-08 04:57:43 +00:00
{
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,
int scale, qse_size_t capa, qse_size_t limit)
2010-09-08 04:57:43 +00:00
{
qse_oht_t* oht;
oht = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_oht_t) + xtnsize);
if (oht == QSE_NULL) return QSE_NULL;
2011-09-01 09:43:46 +00:00
if (qse_oht_init (oht, mmgr, scale, capa, limit) <= -1)
2010-09-08 04:57:43 +00:00
{
QSE_MMGR_FREE (mmgr, oht);
return QSE_NULL;
}
QSE_MEMSET (oht + 1, 0, xtnsize);
2010-09-08 04:57:43 +00:00
return oht;
}
void qse_oht_close (qse_oht_t* oht)
{
qse_oht_fini (oht);
QSE_MMGR_FREE (oht->mmgr, oht);
}
2011-09-01 09:43:46 +00:00
int qse_oht_init (
2010-09-08 04:57:43 +00:00
qse_oht_t* oht, qse_mmgr_t* mmgr,
int scale, qse_size_t capa, qse_size_t limit)
2010-09-08 04:57:43 +00:00
{
qse_size_t i;
if (scale <= 0) scale = 1;
if (capa >= QSE_OHT_NIL - 1) capa = QSE_OHT_NIL - 1;
2010-09-08 04:57:43 +00:00
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);
2011-09-01 09:43:46 +00:00
if (!oht->mark) return -1;
2010-09-08 04:57:43 +00:00
oht->data = QSE_MMGR_ALLOC (mmgr, scale * capa);
if (!oht->data)
{
QSE_MMGR_FREE (mmgr, oht->mark);
2011-09-01 09:43:46 +00:00
return -1;
2010-09-08 04:57:43 +00:00
}
for (i = 0; i < capa; i++) oht->mark[i] = QSE_OHT_EMPTY;
2011-09-01 09:43:46 +00:00
return 0;
2010-09-08 04:57:43 +00:00
}
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_mmgr_t* qse_oht_getmmgr (qse_oht_t* oht)
{
return oht->mmgr;
}
void* qse_oht_getxtn (qse_oht_t* oht)
{
return QSE_XTN (oht);
}
2010-09-08 04:57:43 +00:00
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_NIL;
2010-09-08 04:57:43 +00:00
}
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_NIL && data)
2010-09-08 04:57:43 +00:00
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_NIL)
2010-09-08 04:57:43 +00:00
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_NIL)
2010-09-08 04:57:43 +00:00
{
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_NIL;
2010-09-08 04:57:43 +00:00
/* 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_NIL;
2010-09-08 04:57:43 +00:00
}
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_NIL;
2010-09-08 04:57:43 +00:00
hash = HASH_DATA (oht, data);
/* check if the item already exits */
i = search (oht, data, hash);
if (i != QSE_OHT_NIL) return QSE_OHT_NIL;
2010-09-08 04:57:43 +00:00
/* 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_NIL;
2010-09-08 04:57:43 +00:00
}
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_NIL;
2010-09-08 04:57:43 +00:00
index = search (oht, data, HASH_DATA(oht,data));
if (index != QSE_OHT_NIL)
2010-09-08 04:57:43 +00:00
{
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_NIL;
2010-09-08 04:57:43 +00:00
/* check if the item exists. otherwise, do nothing. */
index = search (oht, data, HASH_DATA(oht,data));
if (index == QSE_OHT_NIL) return QSE_OHT_NIL;
2010-09-08 04:57:43 +00:00
/* 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;
}
}
}