root/usr/src/cmd/isns/isnsd/htable.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#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)

/*
 * external variables.
 */
extern int cache_flag;

/*
 * ****************************************************************************
 * avl_search:
 *      search a node from an AVL tree.
 *
 * tab  - the hash table.
 * uid  - the object UID.
 * return - the node which matches the object UID.
 *
 * ****************************************************************************
 */
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);
}

/*
 * ****************************************************************************
 * avl_search_next:
 *      search a node from an AVL tree, the object UID of the node
 *      is next to the previous object UID.
 *
 * tab  - the hash table.
 * uid  - the previous object UID.
 * return - the next node.
 *
 * ****************************************************************************
 */
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);
}

/*
 * ****************************************************************************
 * avl_ll:
 *      perform LL balance rotation on an AVL tree (or the subtree).
 *
 * a    - the left child.
 * b    - the right child.
 * return - the new root.
 *
 * ****************************************************************************
 */
static htab_itemx_t *
avl_ll(
        htab_itemx_t *a,
        htab_itemx_t *b
)
{
        /* rotate right */
        a->l = b->r;
        a->bf = 0;
        b->r = a;
        b->bf = 0;

        return (b);
}

/*
 * ****************************************************************************
 * avl_rr:
 *      perform RR balance rotation on an AVL tree (or the subtree).
 *
 * a    - the left child.
 * b    - the right child.
 * return - the new root.
 *
 * ****************************************************************************
 */
static htab_itemx_t *
avl_rr(
        htab_itemx_t *a,
        htab_itemx_t *b
)
{
        /* rotate left */
        a->r = b->l;
        a->bf = 0;
        b->l = a;
        b->bf = 0;

        return (b);
}

/*
 * ****************************************************************************
 * avl_lr:
 *      perform LR balance rotation on an AVL tree (or the subtree).
 *
 * a    - the left child.
 * b    - the right child.
 * return - the new root.
 *
 * ****************************************************************************
 */
static htab_itemx_t *
avl_lr(
        htab_itemx_t *a,
        htab_itemx_t *b
)
{
        htab_itemx_t *c;

        c = b->r;

        /* rotate left and then right */
        a->l = c->r;
        c->r = a;
        b->r = c->l;
        c->l = b;

        /* update balance factor */
        switch (c->bf) {
        case -1:
                /* on c's right */
                a->bf = 0;
                b->bf = 1;
                break;
        case 0:
                /* on c itself */
                a->bf = 0;
                b->bf = 0;
                break;
        case 1:
                /* on c's left */
                a->bf = -1;
                b->bf = 0;
                break;
        }
        c->bf = 0;

        return (c);
}

/*
 * ****************************************************************************
 * avl_rl:
 *      perform RL balance rotation on an AVL tree (or the subtree).
 *
 * a    - the left child.
 * b    - the right child.
 * return - the new root.
 *
 * ****************************************************************************
 */
static htab_itemx_t *
avl_rl(
        htab_itemx_t *a,
        htab_itemx_t *b
)
{
        htab_itemx_t *c;

        c = b->l;

        /* rotate right and then left */
        a->r = c->l;
        c->l = a;
        b->l = c->r;
        c->r = b;

        /* update balance factor */
        switch (c->bf) {
        case -1:
                /* on c's right */
                a->bf = 1;
                b->bf = 0;
                break;
        case 0:
                /* on c itself */
                a->bf = 0;
                b->bf = 0;
                break;
        case 1:
                /* on c's left */
                a->bf = 0;
                b->bf = -1;
                break;
        }
        c->bf = 0;

        return (c);
}

/*
 * ****************************************************************************
 * avl_insert:
 *      insert a node into an AVL tree.
 *
 * tab  - the hash table.
 * x    - the node being added.
 *
 * ****************************************************************************
 */
static void
avl_insert(
        htab_t *tab,
        htab_itemx_t *x
)
{
        htab_itemx_t *f, *a, *p, *q, *b, *c;
        int d;

        /* initialize the new one */
        x->bf = 0;
        x->l = NULL;
        x->r = NULL;

        if (tab->avlt == NULL) {
                tab->avlt = x;
        } else {
                /* locate the position */
                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;
                        }
                }
                /* insert it */
                if (x->uid < q->uid) {
                        q->l = x;
                } else {
                        q->r = x;
                }
                /* update the balance factor between a to 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;
                        }
                }
                /* brance is not broken */
                if (a->bf == 0) {
                        a->bf = d;
                        goto bal_done;
                } else if (a->bf + d == 0) {
                        a->bf = 0;
                        goto bal_done;
                }
                /* rotate the tree */
                if (d == 1) {
                        if (b->bf == 1) {
                                /* LL rotate */
                                c = avl_ll(a, b);
                        } else if (b->bf == -1) {
                                /* LR rotate */
                                c = avl_lr(a, b);
                        }
                } else {
                        if (b->bf == -1) {
                                /* RR rotate */
                                c = avl_rr(a, b);
                        } else if (b->bf == 1) {
                                /* RL rotate */
                                c = avl_rl(a, b);
                        }
                }
                /* update the parent */
                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;
        }
}

/*
 * ****************************************************************************
 * new_uid:
 *      allocate new node(s) of the avl tree.
 *
 * tab  - the hash table.
 * uid  - the UID of the node.
 * return - the newly allocated UID node.
 *
 * ****************************************************************************
 */
static htab_itemx_t *
new_uid(
        htab_t *tab,
        uint32_t uid
)
{
        htab_itemx_t *x = NULL;

        uint32_t start, end;

        /* overflow happened */
        if (uid == 0) {
                /* search for an unused one */
                uid ++;
                while (uid != 0 &&
                    avl_search(tab, uid) != NULL) {
                        uid ++;
                }
                if (uid == 0) {
                        /* all are used up, sigh! */
                        return (NULL);
                }
        }

        /* check if there is a gap and the gap needs to be filled up */
        if (uid > tab->buid &&
            (tab->flags & UID_FLAGS_SEQ) != 0) {
                start = tab->buid + 1;
        } else {
                start = uid;
        }
        end = uid;

        /* make new UID(s) */
        do {
                if (x != NULL) {
                        x->hval = BAD_HVAL_MASK;
                        x->t = 0;
                        /* put it to the start of the fifo list */
                        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;
                        /* insert it to the tree */
                        avl_insert(tab, x);
                }
                start ++;
        } while (x != NULL && start <= end && start != 0);

        return (x);
}

/*
 * ****************************************************************************
 * uid_insert:
 *      insert a new UID node to the avl tree.
 *
 * tab  - the hash table.
 * uid_p- the pointer of the UID.
 * hval - the hash value of the new node.
 * return -     0: no UID value assigned;
 *              1: assigned an UID.
 *              -1: no memory.
 *              -2: invalid UID.
 *
 * ****************************************************************************
 */
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) {
                /* search the existing one from the tree */
                x = avl_search(tab, uid);
                if (x == NULL) {
                        x = new_uid(tab, uid);
                } else if (!BAD_HVAL(x->hval) &&
                    x->hval != hval) {
                        /* the item with this uid will override an */
                        /* existing item, we treat this as an error */
                        return (-2);
                }
        } else {
                /* assign a value */
                x = tab->list;
                /* strip off the used ones */
                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) {
                        /* none is available, make a new one */
                        tab->list = x;
                        x = new_uid(tab, tab->buid + 1);
                } else {
                        n = x->n;
                        x->n = NULL;
                        tab->list = n;
                }
                /* update the available list */
                if (tab->list == NULL) {
                        tab->tail = NULL;
                }
                assignx = 1;
                if (x != NULL) {
                        *uid_p = x->uid;
                }
        }

        if (x == NULL) {
                return (-1); /* no memory */
        }

        x->hval = hval;
        x->t = 0; /* registration initial time */

        return (assignx);
}

/*
 * ****************************************************************************
 * enlarge_htab:
 *      enlarge the hash table when it gets too full.
 *
 * tab  - the hash table.
 *
 * ****************************************************************************
 */
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;

        /* enlarge the logsize by one */
        logsize = tab->logsize + 1;
        newsz = (1 << logsize);
        items = (htab_item_t **)calloc(
            newsz * tab->chunks, sizeof (htab_item_t *));
        /* re-hash all items to the new table */
        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.");
        }
}

/*
 * ****************************************************************************
 * htab_init:
 *      some generic initialization for the hash table.
 *
 * ****************************************************************************
 */
void
htab_init(
)
{
        /* do nothing */
}

/*
 * ****************************************************************************
 * htab_create:
 *      create a new hash table.
 *
 * flags - UID_FLAGS_SEQ: the UID in the table needs to be sequential.
 * logsize - the hash table logsize.
 * chunks  - the number of seperated chunks of the table.
 * return  - the newly created hash table.
 *
 * ****************************************************************************
 */
htab_t *
htab_create(
        int flags,
        uint16_t logsize,
        uint16_t chunks
)
{
        htab_t *tab = NULL;
        htab_item_t **items = NULL;
        uint32_t count;

        /* do not enlarge it if the logsize reaches the maximum */
        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; /* reserve one */
                                tab->avlt = NULL;
                                tab->buid = 0;
                                tab->list = NULL;
                                tab->tail = NULL;
                        } else {
                                free(tab);
                                tab = NULL;
                        }
                }
        }

        return (tab);
}

/*
 * ****************************************************************************
 * htab_compute_hval:
 *      compute a hash value for the specified key.
 *
 * key - the key of the hash.
 * return - the hash value.
 *
 * ****************************************************************************
 */
uint32_t
htab_compute_hval(
        const uchar_t *key
)
{
        /* use classic Dan Bernstein hash alorigthm */
        uint32_t hash = 5381;
        int c;

        while ((c = *key++) != 0) {
                hash = ((hash << 5) + hash) + c;
        }

        return (hash);
}

/*
 * ****************************************************************************
 * htab_add:
 *      add an object to the hash table.
 *
 * tab  - the hash table.
 * p    - the object.
 * flag - 0: not an association object; otherwise association object.
 * uid_p- pointer of UID for returning.
 * update_p - pointer of update flag for returning.
 * return - error code.
 *
 * ****************************************************************************
 */
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;

        /* compute the hash value */
        hval = VALID_HVAL(tab->c->get_hval(p, 0, &flags));

        /* check for duplicate */
        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;
        }

        /* add new object */
        if (update_p != NULL) {
                *update_p = 0;
        }

        /* make new items for the object */
        items = (htab_item_t *)calloc(tab->chunks, sizeof (htab_item_t));

        if (items == NULL ||
            tab->count == 0 ||
            (++tab->count) == 0) {
                /* no memory or table is full */
                ec = ISNS_RSP_INTERNAL_ERROR;
                goto add_done;
        }

        /* check if the table needs is too full */
        chunksz = (1 << tab->logsize);
        if (tab->count >= (chunksz * HASH_RATIO) &&
            tab->logsize < MAX_LOGSIZE) {
                enlarge_htab(tab);
                chunksz = (1 << tab->logsize);
        }

        /* put the UID of the object to the avl tree */
        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;
        }

        /* update data store before putting to hash table */
        if (flag == 0) {
                /* not association object */
                ec = tab->c->add_hook(p);
        }

        /* put the object to the table */
        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;
                }
        }

        /* cache has been successfully updated */
        SET_CACHE_UPDATED();

        /* successfully added */
        items = NULL;

        if (ec == 0) {
                /* perform the Default DD behavior */
                tab->c->ddd(p, '+');

                /* set the return uid */
                if (uid_p != NULL) {
                        *uid_p = uid;
                }
        }
add_done:
        if (ec != 0 && items != NULL) {
                free(items);
        }

        return (ec);
}

/*
 * ****************************************************************************
 * htab_remove:
 *      remove an object from the hash table.
 *
 * tab  - the hash table.
 * p    - the lookup control for the object.
 * uid  - the UID of the object.
 * clone_flag - indicate if the removing is for an association object.
 * return - the removed object.
 *
 * ****************************************************************************
 */
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;

        /* get the object hash value */
        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));
        }

        /* search the object from the table */
        flags = 0;
        chunksz = (1 << tab->logsize);
        for (i = 0; ; ) {
                itemp = &tab->items[(i * chunksz) + (hval & tab->mask)];
                item = *itemp;
                while (item != NULL) {
                        /* found it */
                        if (tab->c->cmp(item->p, p, 1) == 0) {
                                /* make an association object if the object */
                                /* has membership in user-defined DD(s). */
                                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) {
                                        /* remove it */
                                        *itemp = item->next;
                                } else if (clone == item->p) {
                                        /* itself is an association object */
                                        goto remove_done;
                                } else {
                                        /* replace it with association */
                                        zhizi = item->p;
                                        item->p = clone;
                                }
                                if (i == 0) {
                                        /* obj has been removed or updated */
                                        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;
                }
        }

        /* update the node in the avl tree */
        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));
                /* mark the uid item as invalid */
                x->hval |= BAD_HVAL_MASK;
                /* update the timestamp */
                x->t = tab->c->timestamp();
                /* put it to the end of fifo list */
                if (tab->list != NULL) {
                        tab->tail->n = x;
                } else {
                        tab->list = x;
                }
                tab->tail = x;
        }

remove_done:
        if (items != NULL) {
                free(items);
        }

        return (zhizi);
}

/*
 * ****************************************************************************
 * htab_lookup:
 *      lookup an object from the hash table.
 *
 * tab  - the hash table.
 * p    - the lookup control for the item.
 * uid  - the UID of the object.
 * uid_p- the pointer of UID for returning.
 * callback - callback function if the object is found.
 * rekey - flag that indicates if the callback function will update
 *              the key of the object.
 * return - error code.
 *
 * ****************************************************************************
 */
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;

        /* compute the hash value */
        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));
        }

        /* find the object */
        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;
                }
        }

        /* found it */
        if (zhizi != NULL) {
                /* set the return uid */
                if (uid_p != NULL) {
                        *uid_p = tab->c->get_uid(zhizi);
                }
                /* invoke callback */
                if (callback != NULL) {
                        ret = callback(zhizi, p);
                }
                if (rekey != 0 && ret == 0) {
                        /* Rekey works for one-chunk hash table only. */
                        ASSERT(tab->chunks == 1 && x != NULL);
                        /* remove from previous slot */
                        *itemp = item->next;
                        /* add it to the new slot */
                        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) {
                /* set the return uid to 0 */
                *uid_p = 0;
        }

        return (ret);
}

/*
 * ****************************************************************************
 * htab_get_next:
 *      get the next object UID from the hash table.
 *
 * tab  - the hash table.
 * uid  - the previous objet UID.
 * return - the next object UID.
 *
 * ****************************************************************************
 */
uint32_t
htab_get_next(
        htab_t *tab,
        uint32_t uid
)
{
        htab_itemx_t *x;

        do {
                /* search the next node from the avl tree */
                x = avl_search_next(tab, uid);
                if (x != NULL) {
                        uid = x->uid;
                        /* validate the node */
                        if (!BAD_HVAL(x->hval)) {
                                return (uid);
                        }
                }
        } while (x != NULL);

        /* no more node is available */
        return (0);
}

/*
 * ****************************************************************************
 * htab_dump:
 *      dump all objects stored in the hash table for debug purpose.
 *
 * tab  - the hash table.
 *
 * ****************************************************************************
 */
#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