#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pthread.h>
#include <libelf.h>
#include <libelf.h>
#include "isns_server.h"
#include "isns_cache.h"
#include "isns_htab.h"
#include "isns_log.h"
#define UID_REUSABLE(T, X) ((T) - (X)->t >= ONE_DAY)
extern int cache_flag;
static htab_itemx_t *
avl_search(
const htab_t *tab,
const uint32_t uid
)
{
htab_itemx_t *x = tab->avlt;
while (x != NULL) {
if (x->uid > uid) {
x = x->l;
} else if (x->uid < uid) {
x = x->r;
} else {
break;
}
}
return (x);
}
static htab_itemx_t *
avl_search_next(
const htab_t *tab,
const uint32_t uid
)
{
htab_itemx_t *p = NULL;
htab_itemx_t *x = tab->avlt;
while (x != NULL) {
if (x->uid > uid) {
p = x;
x = x->l;
} else if (x->uid <= uid) {
x = x->r;
}
}
return (p);
}
static htab_itemx_t *
avl_ll(
htab_itemx_t *a,
htab_itemx_t *b
)
{
a->l = b->r;
a->bf = 0;
b->r = a;
b->bf = 0;
return (b);
}
static htab_itemx_t *
avl_rr(
htab_itemx_t *a,
htab_itemx_t *b
)
{
a->r = b->l;
a->bf = 0;
b->l = a;
b->bf = 0;
return (b);
}
static htab_itemx_t *
avl_lr(
htab_itemx_t *a,
htab_itemx_t *b
)
{
htab_itemx_t *c;
c = b->r;
a->l = c->r;
c->r = a;
b->r = c->l;
c->l = b;
switch (c->bf) {
case -1:
a->bf = 0;
b->bf = 1;
break;
case 0:
a->bf = 0;
b->bf = 0;
break;
case 1:
a->bf = -1;
b->bf = 0;
break;
}
c->bf = 0;
return (c);
}
static htab_itemx_t *
avl_rl(
htab_itemx_t *a,
htab_itemx_t *b
)
{
htab_itemx_t *c;
c = b->l;
a->r = c->l;
c->l = a;
b->l = c->r;
c->r = b;
switch (c->bf) {
case -1:
a->bf = 1;
b->bf = 0;
break;
case 0:
a->bf = 0;
b->bf = 0;
break;
case 1:
a->bf = 0;
b->bf = -1;
break;
}
c->bf = 0;
return (c);
}
static void
avl_insert(
htab_t *tab,
htab_itemx_t *x
)
{
htab_itemx_t *f, *a, *p, *q, *b, *c;
int d;
x->bf = 0;
x->l = NULL;
x->r = NULL;
if (tab->avlt == NULL) {
tab->avlt = x;
} else {
f = NULL;
a = tab->avlt;
p = tab->avlt;
q = NULL;
while (p != NULL) {
if (p->bf != 0) {
a = p;
f = q;
}
q = p;
if (x->uid < q->uid) {
p = p->l;
} else {
p = p->r;
}
}
if (x->uid < q->uid) {
q->l = x;
} else {
q->r = x;
}
if (x->uid < a->uid) {
p = a->l;
d = 1;
} else {
p = a->r;
d = -1;
}
b = p;
while (p != x) {
if (x->uid < p->uid) {
p->bf = 1;
p = p->l;
} else {
p->bf = -1;
p = p->r;
}
}
if (a->bf == 0) {
a->bf = d;
goto bal_done;
} else if (a->bf + d == 0) {
a->bf = 0;
goto bal_done;
}
if (d == 1) {
if (b->bf == 1) {
c = avl_ll(a, b);
} else if (b->bf == -1) {
c = avl_lr(a, b);
}
} else {
if (b->bf == -1) {
c = avl_rr(a, b);
} else if (b->bf == 1) {
c = avl_rl(a, b);
}
}
if (f == NULL) {
tab->avlt = c;
} else if (f->l == a) {
f->l = c;
} else if (f->r == a) {
f->r = c;
}
}
bal_done:
if (x->uid > tab->buid) {
tab->buid = x->uid;
}
}
static htab_itemx_t *
new_uid(
htab_t *tab,
uint32_t uid
)
{
htab_itemx_t *x = NULL;
uint32_t start, end;
if (uid == 0) {
uid ++;
while (uid != 0 &&
avl_search(tab, uid) != NULL) {
uid ++;
}
if (uid == 0) {
return (NULL);
}
}
if (uid > tab->buid &&
(tab->flags & UID_FLAGS_SEQ) != 0) {
start = tab->buid + 1;
} else {
start = uid;
}
end = uid;
do {
if (x != NULL) {
x->hval = BAD_HVAL_MASK;
x->t = 0;
x->n = tab->list;
tab->list = x;
if (tab->tail == NULL) {
tab->tail = x;
}
}
x = (htab_itemx_t *)malloc(sizeof (htab_itemx_t));
if (x != NULL) {
x->uid = start;
x->n = NULL;
avl_insert(tab, x);
}
start ++;
} while (x != NULL && start <= end && start != 0);
return (x);
}
static int
uid_insert(
htab_t *tab,
uint32_t *const uid_p,
const uint32_t hval
)
{
int assignx = 0;
uint32_t uid = *uid_p;
htab_itemx_t *x, *n;
if (uid != 0) {
x = avl_search(tab, uid);
if (x == NULL) {
x = new_uid(tab, uid);
} else if (!BAD_HVAL(x->hval) &&
x->hval != hval) {
return (-2);
}
} else {
x = tab->list;
while (x != NULL &&
!BAD_HVAL(x->hval)) {
n = x->n;
x->n = NULL;
x = n;
}
if (x == NULL ||
UID_REUSABLE(tab->c->timestamp(), x) == 0) {
tab->list = x;
x = new_uid(tab, tab->buid + 1);
} else {
n = x->n;
x->n = NULL;
tab->list = n;
}
if (tab->list == NULL) {
tab->tail = NULL;
}
assignx = 1;
if (x != NULL) {
*uid_p = x->uid;
}
}
if (x == NULL) {
return (-1);
}
x->hval = hval;
x->t = 0;
return (assignx);
}
static void
enlarge_htab(
htab_t *tab
)
{
htab_item_t **items;
uint16_t logsize;
uint32_t oldsz, newsz, mask;
htab_item_t *item, *tmp, **itemp;
uint16_t i;
uint32_t j;
uint32_t uid;
logsize = tab->logsize + 1;
newsz = (1 << logsize);
items = (htab_item_t **)calloc(
newsz * tab->chunks, sizeof (htab_item_t *));
if (items != NULL) {
mask = newsz - 1;
oldsz = (1 << tab->logsize);
i = 0;
while (i < tab->chunks) {
j = 0;
while (j < oldsz) {
item = tab->items[(i * oldsz) + j];
while (item != NULL) {
tmp = item->next;
itemp = &items[(i * newsz) +
(item->hval & mask)];
uid = tab->c->get_uid(item->p);
while (*itemp != NULL &&
tab->c->get_uid((*itemp)->p) >
uid) {
itemp = &(*itemp)->next;
}
item->next = *itemp;
*itemp = item;
item = tmp;
}
j ++;
}
i ++;
}
free(tab->items);
tab->items = items;
tab->logsize = logsize;
tab->mask = mask;
} else {
isnslog(LOG_DEBUG, "enlarge_htab", "calloc() failed.");
}
}
void
htab_init(
)
{
}
htab_t *
htab_create(
int flags,
uint16_t logsize,
uint16_t chunks
)
{
htab_t *tab = NULL;
htab_item_t **items = NULL;
uint32_t count;
if (logsize <= MAX_LOGSIZE &&
chunks > 0) {
tab = (htab_t *)calloc(1, sizeof (htab_t));
if (tab != NULL) {
count = (1 << logsize);
items = (htab_item_t **)calloc(
count * chunks, sizeof (htab_item_t *));
if (items != NULL) {
tab->flags = flags;
tab->items = items;
tab->logsize = logsize;
tab->chunks = chunks;
tab->mask = count - 1;
tab->count = 1;
tab->avlt = NULL;
tab->buid = 0;
tab->list = NULL;
tab->tail = NULL;
} else {
free(tab);
tab = NULL;
}
}
}
return (tab);
}
uint32_t
htab_compute_hval(
const uchar_t *key
)
{
uint32_t hash = 5381;
int c;
while ((c = *key++) != 0) {
hash = ((hash << 5) + hash) + c;
}
return (hash);
}
int
htab_add(
htab_t *tab,
void *p,
int flag,
uint32_t *uid_p,
int *update_p
)
{
int ec = 0;
htab_item_t *items = NULL, **itemp;
uint32_t chunksz;
uint32_t flags = 0;
uint32_t hval;
uint32_t uid = 0;
int i;
hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags));
items = tab->items[hval & tab->mask];
while (items != NULL) {
if (tab->c->cmp(items->p, p, 0) == 0) {
if (flag == 0) {
ec = tab->c->replace_hook(items->p, p, uid_p,
update_p == NULL ? 1 : 0);
}
if (update_p != NULL) {
*update_p = 1;
}
items = NULL;
goto add_done;
}
items = items->next;
}
if (update_p != NULL) {
*update_p = 0;
}
items = (htab_item_t *)calloc(tab->chunks, sizeof (htab_item_t));
if (items == NULL ||
tab->count == 0 ||
(++tab->count) == 0) {
ec = ISNS_RSP_INTERNAL_ERROR;
goto add_done;
}
chunksz = (1 << tab->logsize);
if (tab->count >= (chunksz * HASH_RATIO) &&
tab->logsize < MAX_LOGSIZE) {
enlarge_htab(tab);
chunksz = (1 << tab->logsize);
}
uid = tab->c->get_uid(p);
switch (uid_insert(tab, &uid, hval)) {
case -2:
ec = ISNS_RSP_INVALID_REGIS;
goto add_done;
case -1:
ec = ISNS_RSP_INTERNAL_ERROR;
goto add_done;
case 0:
break;
case 1:
tab->c->set_uid(p, uid);
break;
default:
break;
}
if (flag == 0) {
ec = tab->c->add_hook(p);
}
for (i = 0; ec == 0; ) {
items[i].hval = hval;
items[i].p = p;
itemp = &tab->items[(i * chunksz) + (hval & tab->mask)];
while (*itemp != NULL &&
tab->c->get_uid((*itemp)->p) > uid) {
itemp = &(*itemp)->next;
}
items[i].next = *itemp;
*itemp = &items[i];
i ++;
if (i < tab->chunks) {
hval = VALID_HVAL(tab->c->get_hval(p, i, &flags));
} else {
break;
}
}
SET_CACHE_UPDATED();
items = NULL;
if (ec == 0) {
tab->c->ddd(p, '+');
if (uid_p != NULL) {
*uid_p = uid;
}
}
add_done:
if (ec != 0 && items != NULL) {
free(items);
}
return (ec);
}
isns_obj_t *
htab_remove(
htab_t *tab,
void *p,
uint32_t uid,
int clone_flag
)
{
void *zhizi = NULL;
void *clone = NULL;
htab_item_t *items = NULL;
htab_item_t *item, **itemp;
htab_itemx_t *x = NULL;
uint32_t chunksz;
uint32_t flags;
uint32_t hval;
int i;
if (uid != 0) {
x = avl_search(tab, uid);
if (x != NULL && !BAD_HVAL(x->hval)) {
hval = x->hval;
} else {
goto remove_done;
}
} else {
flags = 0 | FLAGS_CTRL_MASK;
hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags));
}
flags = 0;
chunksz = (1 << tab->logsize);
for (i = 0; ; ) {
itemp = &tab->items[(i * chunksz) + (hval & tab->mask)];
item = *itemp;
while (item != NULL) {
if (tab->c->cmp(item->p, p, 1) == 0) {
if (i == 0) {
if ((clone = tab->c->clone(item->p,
clone_flag)) == NULL) {
tab->c->ddd(item->p, '-');
tab->count --;
items = item;
zhizi = item->p;
}
}
if (clone == NULL) {
*itemp = item->next;
} else if (clone == item->p) {
goto remove_done;
} else {
zhizi = item->p;
item->p = clone;
}
if (i == 0) {
SET_CACHE_UPDATED();
}
break;
}
itemp = &item->next;
item = *itemp;
}
i ++;
if (zhizi != NULL && i < tab->chunks) {
hval = VALID_HVAL(tab->c->get_hval(
zhizi, i, &flags));
} else {
break;
}
}
if (items != NULL) {
if (x == NULL) {
uid = tab->c->get_uid(zhizi);
ASSERT(uid != 0);
x = avl_search(tab, uid);
}
ASSERT(x != NULL && !BAD_HVAL(x->hval));
x->hval |= BAD_HVAL_MASK;
x->t = tab->c->timestamp();
if (tab->list != NULL) {
tab->tail->n = x;
} else {
tab->list = x;
}
tab->tail = x;
}
remove_done:
if (items != NULL) {
free(items);
}
return (zhizi);
}
int
htab_lookup(
htab_t *tab,
void *p,
uint32_t uid,
uint32_t *uid_p,
int (*callback)(void *, void *),
int rekey
)
{
uint32_t ret = 0;
void *zhizi = NULL;
htab_item_t *item, **itemp;
htab_itemx_t *x = NULL;
uint32_t chunksz;
uint32_t flags = 0 | FLAGS_CTRL_MASK;
uint32_t hval;
int i;
if (uid != 0) {
x = avl_search(tab, uid);
if (x != NULL) {
hval = x->hval;
} else {
hval = BAD_HVAL_MASK;
}
} else {
hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags));
}
if (!BAD_HVAL(hval)) {
i = flags & FLAGS_CHUNK_MASK;
chunksz = (1 << tab->logsize);
itemp = &tab->items[(i * chunksz) + (hval & tab->mask)];
item = *itemp;
while (item != NULL) {
if (tab->c->cmp(item->p, p, 1) == 0) {
zhizi = item->p;
break;
}
itemp = &item->next;
item = *itemp;
}
}
if (zhizi != NULL) {
if (uid_p != NULL) {
*uid_p = tab->c->get_uid(zhizi);
}
if (callback != NULL) {
ret = callback(zhizi, p);
}
if (rekey != 0 && ret == 0) {
ASSERT(tab->chunks == 1 && x != NULL);
*itemp = item->next;
flags = 0;
hval = VALID_HVAL(tab->c->get_hval(zhizi, 0, &flags));
x->hval = hval;
item->hval = hval;
itemp = &tab->items[(hval & tab->mask)];
while (*itemp != NULL &&
(tab->c->get_uid((*itemp)->p) >
tab->c->get_uid(zhizi))) {
itemp = &(*itemp)->next;
}
item->next = *itemp;
*itemp = item;
}
} else if (uid_p != NULL) {
*uid_p = 0;
}
return (ret);
}
uint32_t
htab_get_next(
htab_t *tab,
uint32_t uid
)
{
htab_itemx_t *x;
do {
x = avl_search_next(tab, uid);
if (x != NULL) {
uid = x->uid;
if (!BAD_HVAL(x->hval)) {
return (uid);
}
}
} while (x != NULL);
return (0);
}
#ifdef DEBUG
void
htab_dump(
htab_t *tab
)
{
uint32_t chunksz;
htab_item_t *items;
uint32_t i;
chunksz = (1 << tab->logsize);
for (i = 0; i < chunksz; i++) {
items = tab->items[i];
while (items != NULL) {
tab->c->dump(items->p);
items = items->next;
}
}
}
#endif