Skip to content

Commit

Permalink
Merge pull request #1 from funny-falcon/mtbl
Browse files Browse the repository at this point in the history
id_table.c: simple hash with quadratic probing
  • Loading branch information
ko1 committed Aug 6, 2015
2 parents 6682a72 + d1be637 commit 97c6293
Showing 1 changed file with 266 additions and 1 deletion.
267 changes: 266 additions & 1 deletion id_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
*/

#ifndef ID_TABLE_IMPL
#define ID_TABLE_IMPL 10
#define ID_TABLE_IMPL 11
#endif

#if ID_TABLE_IMPL == 0
Expand Down Expand Up @@ -53,6 +53,9 @@
#elif ID_TABLE_IMPL == 10
#define ID_TABLE_USE_COALESCED_HASHING 1

#elif ID_TABLE_IMPL == 11
#define ID_TABLE_USE_SMALL_HASH 1

#else
#error
#endif
Expand Down Expand Up @@ -898,3 +901,265 @@ rb_id_table_foreach_values(sa_table *table, enum rb_id_table_iterator_result (*f
}

#endif /* ID_TABLE_USE_COALESCED_HASHING */

#ifdef ID_TABLE_USE_SMALL_HASH
/* simple open addressing with quadratic probing.
uses mark-bit on collisions - need extra 1 bit,
ID is strictly 3 bits larger than rb_id_serial_t */
typedef ID id_key_t;

static inline ID
key2id(id_key_t key)
{
return rb_id_serial_to_id(key);
}

static inline id_key_t
id2key(ID id)
{
return rb_id_to_serial(id);
}

typedef struct rb_id_item {
id_key_t key;
VALUE val;
} item_t;

struct rb_id_table {
int capa;
int num;
int used;
item_t *items;
};

#define TBL_ID_MASK (~(id_key_t)1)
#define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key >> 1)
#define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key > 1)
#define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].key & 1)
#define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].key |= 1)
static inline void
ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key)
{
tbl->items[i].key = (key << 1) | ITEM_COLLIDED(tbl, i);
}

static inline int
round_capa(int capa) {
/* minsize is 4 */
capa >>= 2;
capa |= capa >> 1;
capa |= capa >> 2;
capa |= capa >> 4;
capa |= capa >> 8;
capa |= capa >> 16;
return (capa + 1) << 2;
}

struct
rb_id_table *rb_id_table_create(size_t capa)
{
struct rb_id_table *tbl = ZALLOC(struct rb_id_table);

if (capa > 0) {
capa = round_capa(capa);
tbl->capa = (int)capa;
tbl->items = ZALLOC_N(item_t, capa);
}
return tbl;
}

void
rb_id_table_free(struct rb_id_table *tbl)
{
xfree(tbl->items);
xfree(tbl);
}

void
rb_id_table_clear(struct rb_id_table *tbl)
{
tbl->num = 0;
tbl->used = 0;
MEMZERO(tbl->items, item_t, tbl->capa);
}

size_t
rb_id_table_size(struct rb_id_table *tbl)
{
return (size_t)tbl->num;
}

size_t
rb_id_table_memsize(struct rb_id_table *tbl)
{
return sizeof(item_t) * tbl->capa + sizeof(struct rb_id_table);
}

static int
table_index(struct rb_id_table* tbl, id_key_t key)
{
if (tbl->capa > 0) {
int mask = tbl->capa - 1;
int ix = key & mask;
int d = 1;
while (key != ITEM_GET_KEY(tbl, ix)) {
if (!ITEM_COLLIDED(tbl, ix))
return -1;
ix = (ix + d) & mask;
d++;
}
return ix;
} else {
return -1;
}
}

static void
table_raw_insert(struct rb_id_table *tbl, id_key_t key, VALUE val)
{
int mask = tbl->capa - 1;
int ix = key & mask;
int d = 1;
while (ITEM_KEY_ISSET(tbl, ix)) {
ITEM_SET_COLLIDED(tbl, ix);
ix = (ix + d) & mask;
d++;
}
tbl->num++;
if (ITEM_COLLIDED(tbl, ix) == 0) {
tbl->used++;
}
ITEM_SET_KEY(tbl, ix, key);
tbl->items[ix].val = val;
}

static int
id_table_delete(struct rb_id_table *tbl, int index)
{
if (index >= 0) {
ITEM_SET_KEY(tbl, index, 0);
tbl->items[index].val = 0;
if (ITEM_COLLIDED(tbl, index) == 0) {
tbl->used--;
}
tbl->num--;
return TRUE;
} else {
return FALSE;
}
}

static void
table_extend(struct rb_id_table* tbl)
{
if (tbl->used + (tbl->used >> 1) >= tbl->capa) {
int new_cap = round_capa(tbl->num + (tbl->num >> 1));
int i;
item_t* old;
struct rb_id_table tmp_tbl = {new_cap, 0, 0};
tmp_tbl.items = ZALLOC_N(item_t, new_cap);
for (i = 0; i < tbl->capa; i++) {
id_key_t key = ITEM_GET_KEY(tbl, i);
if (key != 0) {
table_raw_insert(&tmp_tbl, key, tbl->items[i].val);
}
}
old = tbl->items;
*tbl = tmp_tbl;
xfree(old);
}
}

#if ID_TABLE_DEBUG
static void
tbl_show(struct rb_id_table *tbl)
{
const id_key_t *keys = tbl->keys;
const int capa = tbl->capa;
int i;

fprintf(stderr, "tbl: %p (capa: %d, num: %d, used: %d)\n", tbl, tbl->capa, tbl->num, tbl->used);
for (i=0; i<capa; i++) {
if (ITEM_KEY_ISSET(tbl, i)) {
fprintf(stderr, " -> [%d] %s %d\n", i, rb_id2name(key2id(keys[i])), (int)keys[i]);
}
}
}
#endif

int
rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
{
id_key_t key = id2key(id);
int index = table_index(tbl, key);

if (index >= 0) {
*valp = tbl->items[index].val;
return TRUE;
}
else {
return FALSE;
}
}

int
rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val)
{
const id_key_t key = id2key(id);
const int index = table_index(tbl, key);

if (index >= 0) {
tbl->items[index].val = val;
}
else {
table_extend(tbl);
table_raw_insert(tbl, key, val);
}
return TRUE;
}

int
rb_id_table_delete(struct rb_id_table *tbl, ID id)
{
const id_key_t key = id2key(id);
int index = table_index(tbl, key);
return id_table_delete(tbl, index);
}

void
rb_id_table_foreach(struct rb_id_table *tbl, enum rb_id_table_iterator_result (*func)(ID id, VALUE val, void *data), void *data)
{
int i, capa = tbl->capa;

for (i=0; i<capa; i++) {
if (ITEM_KEY_ISSET(tbl, i)) {
const id_key_t key = ITEM_GET_KEY(tbl, i);
enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
assert(key != 0);

if (ret == ID_TABLE_DELETE)
id_table_delete(tbl, i);
else if (ret == ID_TABLE_STOP)
return;
}
}
}

void
rb_id_table_foreach_values(struct rb_id_table *tbl, enum rb_id_table_iterator_result (*func)(VALUE val, void *data), void *data)
{
int i, capa = tbl->capa;

for (i=0; i<capa; i++) {
if (ITEM_KEY_ISSET(tbl, i)) {
enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
assert(key != 0);

if (ret == ID_TABLE_DELETE)
id_table_delete(tbl, i);
else if (ret == ID_TABLE_STOP)
return;
}
}
}
#endif /* ID_TABLE_USE_SMALL_HASH */

0 comments on commit 97c6293

Please sign in to comment.