#include "config.h"
#include "testcode/unitmain.h"
#include "util/log.h"
#include "util/storage/slabhash.h"
typedef struct slabhash_testkey testkey_t;
typedef struct slabhash_testdata testdata_t;
static void delkey(struct slabhash_testkey* k) {
lock_rw_destroy(&k->entry.lock); free(k);}
static hashvalue_t myhash(int id) {
hashvalue_t h = (hashvalue_t)id & 0x0f;
h |= (h << 28);
return h;
}
static testkey_t* newkey(int id) {
testkey_t* k = (testkey_t*)calloc(1, sizeof(testkey_t));
if(!k) fatal_exit("out of memory");
k->id = id;
k->entry.hash = myhash(id);
k->entry.key = k;
lock_rw_init(&k->entry.lock);
return k;
}
static testdata_t* newdata(int val) {
testdata_t* d = (testdata_t*)calloc(1,
sizeof(testdata_t));
if(!d) fatal_exit("out of memory");
d->data = val;
return d;
}
static void
test_short_table(struct slabhash* table)
{
testkey_t* k = newkey(12);
testkey_t* k2 = newkey(14);
testdata_t* d = newdata(128);
testdata_t* d2 = newdata(129);
k->entry.data = d;
k2->entry.data = d2;
slabhash_insert(table, myhash(12), &k->entry, d, NULL);
slabhash_insert(table, myhash(14), &k2->entry, d2, NULL);
unit_assert( slabhash_lookup(table, myhash(12), k, 0) == &k->entry);
lock_rw_unlock( &k->entry.lock );
unit_assert( slabhash_lookup(table, myhash(14), k2, 0) == &k2->entry);
lock_rw_unlock( &k2->entry.lock );
slabhash_remove(table, myhash(12), k);
slabhash_remove(table, myhash(14), k2);
}
#define HASHTESTMAX 32
static void
testadd(struct slabhash* table, testdata_t* ref[])
{
int numtoadd = random() % HASHTESTMAX;
testdata_t* data = newdata(numtoadd);
testkey_t* key = newkey(numtoadd);
key->entry.data = data;
slabhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
ref[numtoadd] = data;
}
static void
testremove(struct slabhash* table, testdata_t* ref[])
{
int num = random() % HASHTESTMAX;
testkey_t* key = newkey(num);
slabhash_remove(table, myhash(num), key);
ref[num] = NULL;
delkey(key);
}
static void
testlookup(struct slabhash* table, testdata_t* ref[])
{
int num = random() % HASHTESTMAX;
testkey_t* key = newkey(num);
struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
testdata_t* data = en? (testdata_t*)en->data : NULL;
if(en) {
unit_assert(en->key);
unit_assert(en->data);
}
if(0) log_info("lookup %d got %d, expect %d", num, en? data->data :-1,
ref[num]? ref[num]->data : -1);
unit_assert( data == ref[num] );
if(en) { lock_rw_unlock(&en->lock); }
delkey(key);
}
static void
check_lru_table(struct lruhash* table)
{
struct lruhash_entry* p;
size_t c = 0;
lock_quick_lock(&table->lock);
unit_assert( table->num <= table->size);
unit_assert( table->size_mask == (int)table->size-1 );
unit_assert( (table->lru_start && table->lru_end) ||
(!table->lru_start && !table->lru_end) );
unit_assert( table->space_used <= table->space_max );
if(table->lru_start)
unit_assert(table->lru_start->lru_prev == NULL);
if(table->lru_end)
unit_assert(table->lru_end->lru_next == NULL);
p = table->lru_start;
while(p) {
if(p->lru_prev) {
unit_assert(p->lru_prev->lru_next == p);
}
if(p->lru_next) {
unit_assert(p->lru_next->lru_prev == p);
}
c++;
p = p->lru_next;
}
unit_assert(c == table->num);
unit_assert( table->space_used ==
table->num * test_slabhash_sizefunc(NULL, NULL) );
lock_quick_unlock(&table->lock);
}
static void
check_table(struct slabhash* table)
{
size_t i;
for(i=0; i<table->size; i++)
check_lru_table(table->array[i]);
}
static void
testadd_unlim(struct slabhash* table, testdata_t** ref)
{
int numtoadd = random() % (HASHTESTMAX * 10);
testdata_t* data = newdata(numtoadd);
testkey_t* key = newkey(numtoadd);
key->entry.data = data;
slabhash_insert(table, myhash(numtoadd), &key->entry, data, NULL);
if(ref)
ref[numtoadd] = data;
}
static void
testremove_unlim(struct slabhash* table, testdata_t** ref)
{
int num = random() % (HASHTESTMAX*10);
testkey_t* key = newkey(num);
slabhash_remove(table, myhash(num), key);
if(ref)
ref[num] = NULL;
delkey(key);
}
static void
testlookup_unlim(struct slabhash* table, testdata_t** ref)
{
int num = random() % (HASHTESTMAX*10);
testkey_t* key = newkey(num);
struct lruhash_entry* en = slabhash_lookup(table, myhash(num), key, 0);
testdata_t* data = en? (testdata_t*)en->data : NULL;
if(en) {
unit_assert(en->key);
unit_assert(en->data);
}
if(0 && ref) log_info("lookup unlim %d got %d, expect %d", num, en ?
data->data :-1, ref[num] ? ref[num]->data : -1);
if(data && ref) {
unit_assert( data == ref[num] );
}
if(en) { lock_rw_unlock(&en->lock); }
delkey(key);
}
static void
test_long_table(struct slabhash* table)
{
testdata_t* ref[HASHTESTMAX * 100];
size_t i;
memset(ref, 0, sizeof(ref));
if(0) slabhash_status(table, "unit test", 1);
srandom(48);
for(i=0; i<1000; i++) {
if(i == 500) {
slabhash_clear(table);
memset(ref, 0, sizeof(ref));
continue;
}
switch(random() % 4) {
case 0:
case 3:
testadd(table, ref);
break;
case 1:
testremove(table, ref);
break;
case 2:
testlookup(table, ref);
break;
default:
unit_assert(0);
}
if(0) slabhash_status(table, "unit test", 1);
check_table(table);
}
for(i=0; i<1000; i++) {
switch(random() % 4) {
case 0:
case 3:
testadd_unlim(table, ref);
break;
case 1:
testremove_unlim(table, ref);
break;
case 2:
testlookup_unlim(table, ref);
break;
default:
unit_assert(0);
}
if(0) slabhash_status(table, "unlim", 1);
check_table(table);
}
}
struct slab_test_thr {
int num;
ub_thread_t id;
struct slabhash* table;
};
static void*
test_thr_main(void* arg)
{
struct slab_test_thr* t = (struct slab_test_thr*)arg;
int i;
log_thread_set(&t->num);
for(i=0; i<1000; i++) {
switch(random() % 4) {
case 0:
case 3:
testadd_unlim(t->table, NULL);
break;
case 1:
testremove_unlim(t->table, NULL);
break;
case 2:
testlookup_unlim(t->table, NULL);
break;
default:
unit_assert(0);
}
if(0) slabhash_status(t->table, "hashtest", 1);
if(i % 100 == 0)
check_table(t->table);
}
check_table(t->table);
return NULL;
}
static void
test_threaded_table(struct slabhash* table)
{
int numth = 10;
struct slab_test_thr t[100];
int i;
for(i=1; i<numth; i++) {
t[i].num = i;
t[i].table = table;
ub_thread_create(&t[i].id, test_thr_main, &t[i]);
}
for(i=1; i<numth; i++) {
ub_thread_join(t[i].id);
}
if(0) slabhash_status(table, "hashtest", 1);
}
void slabhash_test(void)
{
struct slabhash* table;
unit_show_feature("slabhash");
table = slabhash_create(4, 2, 10400,
test_slabhash_sizefunc, test_slabhash_compfunc,
test_slabhash_delkey, test_slabhash_deldata, NULL);
test_short_table(table);
test_long_table(table);
slabhash_delete(table);
table = slabhash_create(4, 2, 10400,
test_slabhash_sizefunc, test_slabhash_compfunc,
test_slabhash_delkey, test_slabhash_deldata, NULL);
test_threaded_table(table);
slabhash_delete(table);
}