Annotation of embedaddon/libpdel/util/ghash.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (c) 2001-2002 Packet Design, LLC.
        !             4:  * All rights reserved.
        !             5:  * 
        !             6:  * Subject to the following obligations and disclaimer of warranty,
        !             7:  * use and redistribution of this software, in source or object code
        !             8:  * forms, with or without modifications are expressly permitted by
        !             9:  * Packet Design; provided, however, that:
        !            10:  * 
        !            11:  *    (i)  Any and all reproductions of the source or object code
        !            12:  *         must include the copyright notice above and the following
        !            13:  *         disclaimer of warranties; and
        !            14:  *    (ii) No rights are granted, in any manner or form, to use
        !            15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
        !            16:  *         on advertising, endorsements, or otherwise except as such
        !            17:  *         appears in the above copyright notice or in the software.
        !            18:  * 
        !            19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
        !            20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
        !            21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
        !            22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
        !            23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
        !            24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
        !            25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
        !            26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
        !            27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
        !            28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
        !            29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
        !            30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
        !            31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
        !            32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
        !            33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
        !            35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
        !            36:  * THE POSSIBILITY OF SUCH DAMAGE.
        !            37:  *
        !            38:  * Author: Archie Cobbs <archie@freebsd.org>
        !            39:  */
        !            40: 
        !            41: #include <sys/types.h>
        !            42: #include <sys/param.h>
        !            43: #include <netinet/in.h>
        !            44: 
        !            45: #ifndef _KERNEL
        !            46: #include <stdio.h>
        !            47: #include <stdlib.h>
        !            48: #include <stdarg.h>
        !            49: #include <syslog.h>
        !            50: #include <errno.h>
        !            51: #include <assert.h>
        !            52: #include <string.h>
        !            53: #include <err.h>
        !            54: #else
        !            55: #include <sys/systm.h>
        !            56: #include <sys/syslog.h>
        !            57: #endif
        !            58: 
        !            59: #include "structs/structs.h"
        !            60: #include "structs/type/array.h"
        !            61: 
        !            62: #include "util/ghash.h"
        !            63: #include "util/typed_mem.h"
        !            64: 
        !            65: #define MIN_BUCKETS                    31
        !            66: #define DEFAULT_MAX_LOAD               75      /* 75% full */
        !            67: #define MIN_LOAD_FACTOR                        20      /* 1/20 of current size */
        !            68: 
        !            69: struct gent {
        !            70:        const void      *item;                  /* this item */
        !            71:        struct gent     *next;                  /* next item in bucket */
        !            72: };
        !            73: 
        !            74: struct ghash {
        !            75:        void            *arg;
        !            76:        ghash_hash_t    *hash;
        !            77:        ghash_equal_t   *equal;
        !            78:        ghash_add_t     *add;
        !            79:        ghash_del_t     *del;
        !            80:        u_int           maxload;                /* maximum load factor in % */
        !            81:        u_int           mods;                   /* modification counter */
        !            82:        u_int           size;                   /* number of items in table */
        !            83:        u_int           maxsize;                /* when we need to increase */
        !            84:        u_int           minsize;                /* when we need to decrease */
        !            85:        u_int           nbuckets;               /* number of buckets in table */
        !            86:        u_int           iter_count;             /* number outstanding iter's */
        !            87:        struct gent     **buckets;              /* hash bucket array */
        !            88:        struct {                                /* typed memory alloc types */
        !            89:            const char  *g;
        !            90:            char        g_buf[TYPED_MEM_TYPELEN];
        !            91:            const char  *gent;
        !            92:            char        gent_buf[TYPED_MEM_TYPELEN];
        !            93:            const char  *iter;
        !            94:            char        iter_buf[TYPED_MEM_TYPELEN];
        !            95:            const char  *buckets;
        !            96:            char        buckets_buf[TYPED_MEM_TYPELEN];
        !            97:        }               mtype;
        !            98:        u_char          locked;
        !            99: };
        !           100: 
        !           101: /*
        !           102:  * Internal functions
        !           103:  */
        !           104: 
        !           105: static ghash_equal_t   ghash_default_equal;
        !           106: static ghash_hash_t    ghash_default_hash;
        !           107: static ghash_add_t     ghash_default_add;
        !           108: static ghash_del_t     ghash_default_del;
        !           109: 
        !           110: static void    ghash_rehash(struct ghash *g, u_int new_nbuckets);
        !           111: 
        !           112: /* Update maxsize and minsize after nbuckets changes */
        !           113: #define UPDATE_SIZES(g)                                                        \
        !           114:        do {                                                            \
        !           115:                (g)->maxsize = ((g)->nbuckets * (g)->maxload) / 100;    \
        !           116:                if ((g)->maxsize == 0)                                  \
        !           117:                        (g)->maxsize = 1;                               \
        !           118:                (g)->minsize = (g)->nbuckets / MIN_LOAD_FACTOR;         \
        !           119:        } while (0)
        !           120: 
        !           121: /*
        !           122:  * Create a new hash table.
        !           123:  */
        !           124: struct ghash *
        !           125: ghash_create(void *arg, u_int isize, u_int maxload, const char *mtype,
        !           126:        ghash_hash_t *hash, ghash_equal_t *equal, ghash_add_t *add,
        !           127:        ghash_del_t *del)
        !           128: {
        !           129:        struct ghash *g;
        !           130: 
        !           131:        /* Apply defaults and sanity check */
        !           132:        if (isize < MIN_BUCKETS)
        !           133:                isize = MIN_BUCKETS;
        !           134:        if (maxload == 0)
        !           135:                maxload = DEFAULT_MAX_LOAD;
        !           136: 
        !           137:        /* Create new hash table object */
        !           138:        if ((g = MALLOC(mtype, sizeof(*g))) == NULL)
        !           139:                return (NULL);
        !           140:        memset(g, 0, sizeof(*g));
        !           141:        g->arg = arg;
        !           142:        g->hash = hash != NULL ? hash : ghash_default_hash;
        !           143:        g->equal = equal != NULL ? equal : ghash_default_equal;
        !           144:        g->add = add != NULL ? add : ghash_default_add;
        !           145:        g->del = del != NULL ? del: ghash_default_del;
        !           146:        g->nbuckets = isize;
        !           147:        g->maxload = maxload;
        !           148:        UPDATE_SIZES(g);
        !           149: 
        !           150:        /* Create memory subtypes */
        !           151:        if (mtype != NULL) {
        !           152:                snprintf(g->mtype.g_buf, sizeof(g->mtype.g_buf), "%s", mtype);
        !           153:                g->mtype.g = g->mtype.g_buf;
        !           154:                snprintf(g->mtype.gent_buf, sizeof(g->mtype.gent_buf),
        !           155:                    "%s.gent", mtype);
        !           156:                g->mtype.gent = g->mtype.gent_buf;
        !           157:                snprintf(g->mtype.iter_buf, sizeof(g->mtype.iter_buf),
        !           158:                    "%s.iter", mtype);
        !           159:                g->mtype.iter = g->mtype.iter_buf;
        !           160:                snprintf(g->mtype.buckets_buf, sizeof(g->mtype.buckets_buf),
        !           161:                    "%s.buckets", mtype);
        !           162:                g->mtype.buckets = g->mtype.buckets_buf;
        !           163:        }
        !           164: 
        !           165:        /* Allocate initial bucket array */
        !           166:        if ((g->buckets = MALLOC(g->mtype.buckets,
        !           167:            g->nbuckets * sizeof(*g->buckets))) == NULL) {
        !           168:                FREE(g->mtype.g, g);
        !           169:                return (NULL);
        !           170:        }
        !           171:        memset(g->buckets, 0, g->nbuckets * sizeof(*g->buckets));
        !           172: 
        !           173:        /* Done */
        !           174:        return (g);
        !           175: }
        !           176: 
        !           177: /*
        !           178:  * Destroy a hash table.
        !           179:  */
        !           180: void
        !           181: ghash_destroy(struct ghash **gp)
        !           182: {
        !           183:        struct ghash *const g = *gp;
        !           184:        u_int i;
        !           185: 
        !           186:        if (g == NULL)
        !           187:                return;
        !           188:        assert(!g->locked);
        !           189:        assert(g->iter_count == 0);
        !           190:        g->locked = 1;
        !           191:        for (i = 0; i < g->nbuckets; i++) {
        !           192:                while (g->buckets[i] != NULL) {
        !           193:                        struct gent *const e = g->buckets[i];
        !           194:                        const void *const item = e->item;
        !           195: 
        !           196:                        g->buckets[i] = e->next;
        !           197:                        FREE(g->mtype.gent, e);
        !           198:                        g->size--;
        !           199:                        (*g->del)(g, (void *)item);
        !           200:                }
        !           201:        }
        !           202:        FREE(g->mtype.buckets, g->buckets);
        !           203:        FREE(g->mtype.g, g);
        !           204:        *gp = NULL;
        !           205: }
        !           206: 
        !           207: /*
        !           208:  * Get the argument supplied to ghash_create().
        !           209:  */
        !           210: void *
        !           211: ghash_arg(struct ghash *g)
        !           212: {
        !           213:        return (g->arg);
        !           214: }
        !           215: 
        !           216: /*
        !           217:  * Return number of items in the table.
        !           218:  */
        !           219: u_int
        !           220: ghash_size(struct ghash *g)
        !           221: {
        !           222:        return (g->size);
        !           223: }
        !           224: 
        !           225: /*
        !           226:  * Get an item.
        !           227:  *
        !           228:  * Returns the item, or NULL if the item does not exist.
        !           229:  */
        !           230: void *
        !           231: ghash_get(struct ghash *g, const void *item)
        !           232: {
        !           233:        struct gent *e;
        !           234: 
        !           235:        if (item == NULL)
        !           236:                return (NULL);
        !           237:        for (e = g->buckets[(*g->hash)(g, item) % g->nbuckets];
        !           238:            e != NULL; e = e->next) {
        !           239:                if (item == e->item || (*g->equal)(g, item, e->item))
        !           240:                        return ((void *)e->item);
        !           241:        }
        !           242:        return (NULL);
        !           243: }
        !           244: 
        !           245: /*
        !           246:  * Put an item.
        !           247:  *
        !           248:  * Returns 0 if the item is new, 1 if it replaces an existing
        !           249:  * item, and -1 if there was an error.
        !           250:  */
        !           251: int
        !           252: ghash_put(struct ghash *g, const void *item)
        !           253: {
        !           254:        struct gent **start;
        !           255:        struct gent *e;
        !           256: 
        !           257:        /* Sanity check */
        !           258:        if (item == NULL) {
        !           259:                errno = EINVAL;
        !           260:                return (-1);
        !           261:        }
        !           262: 
        !           263:        /* Lock hash table */
        !           264:        if (g->locked) {
        !           265:                errno = EBUSY;
        !           266:                return (-1);
        !           267:        }
        !           268:        g->locked = 1;
        !           269: 
        !           270:        /* Find item's bucket */
        !           271:        start = &g->buckets[(*g->hash)(g, item) % g->nbuckets];
        !           272: 
        !           273:        /* See if item already exists, and replace it if so */
        !           274:        for (e = *start; e != NULL; e = e->next) {
        !           275:                if ((*g->equal)(g, item, e->item)) {
        !           276:                        (*g->del)(g, (void *)e->item);
        !           277:                        e->item = item;
        !           278:                        (*g->add)(g, (void *)e->item);
        !           279:                        g->mods++;
        !           280:                        g->locked = 0;
        !           281:                        return (1);
        !           282:                }
        !           283:        }
        !           284: 
        !           285:        /* Expand table if necessary */
        !           286:        if (g->size + 1 > g->maxsize) {
        !           287:                ghash_rehash(g, (g->nbuckets * 2) - 1);
        !           288:                start = &g->buckets[(*g->hash)(g, item) % g->nbuckets];
        !           289:        }
        !           290:        g->mods++;
        !           291: 
        !           292:        /* Add new item */
        !           293:        if ((e = MALLOC(g->mtype.gent, sizeof(*e))) == NULL) {
        !           294:                g->locked = 0;
        !           295:                return (-1);
        !           296:        }
        !           297:        (*g->add)(g, (void *)item);
        !           298:        e->item = item;
        !           299:        e->next = *start;
        !           300:        *start = e;
        !           301:        g->size++;
        !           302: 
        !           303:        /* Unlock tree and return */
        !           304:        g->locked = 0;
        !           305:        return (0);
        !           306: }
        !           307: 
        !           308: /*
        !           309:  * Remove an item.
        !           310:  *
        !           311:  * Returns 1 if the item was found and removed, 0 if not found.
        !           312:  */
        !           313: int
        !           314: ghash_remove(struct ghash *g, const void *item)
        !           315: {
        !           316:        struct gent **ep;
        !           317:        int found = 0;
        !           318: 
        !           319:        /* Sanity check */
        !           320:        if (item == NULL)
        !           321:                return (0);
        !           322: 
        !           323:        /* Lock hash table */
        !           324:        if (g->locked) {
        !           325:                errno = EBUSY;
        !           326:                return (-1);
        !           327:        }
        !           328:        g->locked = 1;
        !           329: 
        !           330:        /* Find item */
        !           331:        for (ep = &g->buckets[(*g->hash)(g, item) % g->nbuckets];
        !           332:            *ep != NULL; ep = &(*ep)->next) {
        !           333:                struct gent *const e = *ep;
        !           334: 
        !           335:                if ((*g->equal)(g, item, e->item)) {
        !           336:                        const void *const oitem = e->item;
        !           337: 
        !           338:                        *ep = e->next;
        !           339:                        FREE(g->mtype.gent, e);
        !           340:                        g->size--;
        !           341:                        (*g->del)(g, (void *)oitem);
        !           342:                        found = 1;
        !           343:                        break;
        !           344:                }
        !           345:        }
        !           346:        if (!found) {
        !           347:                g->locked = 0;
        !           348:                return (0);
        !           349:        }
        !           350:        g->mods++;
        !           351: 
        !           352:        /* Shrink table if desired */
        !           353:        if (g->size < g->minsize && g->size > MIN_BUCKETS) {
        !           354:                u_int new_size;
        !           355: 
        !           356:                new_size = (g->size * g->maxload) / 200;
        !           357:                if (new_size > MIN_BUCKETS)
        !           358:                        new_size = MIN_BUCKETS;
        !           359:                ghash_rehash(g, new_size);
        !           360:        }
        !           361: 
        !           362:        /* Unlock tree and return */
        !           363:        g->locked = 0;
        !           364:        return (1);
        !           365: }
        !           366: 
        !           367: /**********************************************************************
        !           368:                        ITERATOR METHODS
        !           369: **********************************************************************/
        !           370: 
        !           371: struct ghash_iter {
        !           372:        struct ghash    *g;             /* hash table we're iterating over */
        !           373:        u_int           mods;           /* guard against changes to table */
        !           374:        u_int           bucket;         /* current bucket we're traversing */
        !           375:        u_int           count;          /* number of items returned so far */
        !           376:        struct gent     **ep;           /* points to previous rtn'd by next() */
        !           377: };
        !           378: 
        !           379: struct ghash_iter *
        !           380: ghash_iter_create(struct ghash *g)
        !           381: {
        !           382:        struct ghash_iter *iter;
        !           383: 
        !           384:        if (g == NULL) {
        !           385:                errno = EINVAL;
        !           386:                return (NULL);
        !           387:        }
        !           388:        if ((iter = MALLOC(g->mtype.iter, sizeof(*iter))) == NULL)
        !           389:                return (NULL);
        !           390:        memset(iter, 0, sizeof(*iter));
        !           391:        iter->g = g;
        !           392:        iter->mods = g->mods;
        !           393:        g->iter_count++;
        !           394:        return (iter);
        !           395: }
        !           396: 
        !           397: void
        !           398: ghash_iter_destroy(struct ghash_iter **iterp)
        !           399: {
        !           400:        struct ghash_iter *const iter = *iterp;
        !           401: 
        !           402:        if (iter == NULL)
        !           403:                return;
        !           404:        iter->g->iter_count--;
        !           405:        FREE(iter->g->mtype.iter, iter);
        !           406:        *iterp = NULL;
        !           407: }
        !           408: 
        !           409: int
        !           410: ghash_iter_has_next(struct ghash_iter *iter)
        !           411: {
        !           412:        struct ghash *const g = iter->g;
        !           413: 
        !           414:        if (iter->mods != g->mods)
        !           415:                return (1);                     /* force a call to next() */
        !           416:        return (iter->count < g->size);
        !           417: }
        !           418: 
        !           419: /*
        !           420:  * Return next element in an iteration.
        !           421:  */
        !           422: void *
        !           423: ghash_iter_next(struct ghash_iter *iter)
        !           424: {
        !           425:        struct ghash *const g = iter->g;
        !           426: 
        !           427:        /* Sanity checks */
        !           428:        if (iter->mods != g->mods || iter->count >= g->size) {
        !           429:                errno = EINVAL;
        !           430:                return (NULL);
        !           431:        }
        !           432: 
        !           433:        /* Advance pointer to next element */
        !           434:        if (iter->count++ == 0)
        !           435:                iter->ep = &g->buckets[0];              /* initialize pointer */
        !           436:        else {
        !           437:                assert(*iter->ep != NULL);
        !           438:                iter->ep = &(*iter->ep)->next;
        !           439:        }
        !           440:        while (*iter->ep == NULL) {
        !           441:                iter->ep = &g->buckets[++iter->bucket];
        !           442:                assert(iter->bucket < g->nbuckets);
        !           443:        }
        !           444: 
        !           445:        /* Update item count and return next item */
        !           446:        return ((void *)(*iter->ep)->item);
        !           447: }
        !           448: 
        !           449: /*
        !           450:  * Remove previously returned element in an iteration.
        !           451:  */
        !           452: int
        !           453: ghash_iter_remove(struct ghash_iter *iter)
        !           454: {
        !           455:        struct ghash *const g = iter->g;
        !           456:        const void *item;
        !           457:        struct gent *e;
        !           458: 
        !           459:        /* Sanity checks */
        !           460:        if (iter->count == 0 || iter->mods != g->mods) {
        !           461:                errno = EINVAL;
        !           462:                return (-1);
        !           463:        }
        !           464: 
        !           465:        /* Lock hash table */
        !           466:        if (g->locked) {
        !           467:                errno = EBUSY;
        !           468:                return (-1);
        !           469:        }
        !           470:        g->locked = 1;
        !           471: 
        !           472:        /* Remove element */
        !           473:        e = *iter->ep;
        !           474:        item = e->item;
        !           475:        *iter->ep = e->next;
        !           476:        FREE(g->mtype.gent, e);
        !           477:        g->size--;
        !           478:        iter->count--;
        !           479:        g->mods++;
        !           480:        iter->mods++;
        !           481:        (*g->del)(g, (void *)item);
        !           482: 
        !           483:        /* Shrink table if desired */
        !           484:        if (g->size < g->minsize && g->size > MIN_BUCKETS) {
        !           485:                u_int new_size;
        !           486: 
        !           487:                new_size = (g->size * g->maxload) / 200;
        !           488:                if (new_size > MIN_BUCKETS)
        !           489:                        new_size = MIN_BUCKETS;
        !           490:                ghash_rehash(g, new_size);
        !           491:        }
        !           492:        g->locked = 0;
        !           493:        return (0);
        !           494: }
        !           495: 
        !           496: /*
        !           497:  * Get an array of hash table contents, optionally sorted.
        !           498:  */
        !           499: int
        !           500: ghash_dump(struct ghash *g, void ***listp, const char *mtype)
        !           501: {
        !           502:        struct gent *e;
        !           503:        void **list;
        !           504:        u_int num;
        !           505:        u_int i;
        !           506: 
        !           507:        /* Get items in a list */
        !           508:        if ((list = MALLOC(mtype, g->size * sizeof(*list))) == NULL)
        !           509:                return (-1);
        !           510:        for (num = i = 0; i < g->nbuckets; i++) {
        !           511:                for (e = g->buckets[i]; e != NULL; e = e->next)
        !           512:                        list[num++] = (void *)e->item;
        !           513:        }
        !           514:        assert(num == g->size);
        !           515: 
        !           516:        /* Done */
        !           517:        *listp = list;
        !           518:        return (num);
        !           519: }
        !           520: 
        !           521: /*
        !           522:  * Start a hash table walk.
        !           523:  */
        !           524: void
        !           525: ghash_walk_init(struct ghash *g, struct ghash_walk *walk)
        !           526: {
        !           527:        memset(walk, 0, sizeof(*walk));
        !           528:        walk->mods = g->mods;
        !           529: }
        !           530: 
        !           531: /*
        !           532:  * Get the next item in the hash table walk.
        !           533:  */
        !           534: void *
        !           535: ghash_walk_next(struct ghash *g, struct ghash_walk *walk)
        !           536: {
        !           537:        void *item;
        !           538: 
        !           539:        /* Check for modifications */
        !           540:        if (g->mods != walk->mods) {
        !           541:                errno = EINVAL;
        !           542:                return (NULL);
        !           543:        }
        !           544: 
        !           545:        /* Go to next bucket if needed */
        !           546:        if (walk->e == NULL) {
        !           547:                while (walk->bucket < g->nbuckets
        !           548:                    && g->buckets[walk->bucket] == NULL)
        !           549:                        walk->bucket++;
        !           550:                if (walk->bucket == g->nbuckets) {
        !           551:                        errno = ENOENT;
        !           552:                        return (NULL);
        !           553:                }
        !           554:                walk->e = g->buckets[walk->bucket++];
        !           555:        }
        !           556: 
        !           557:        /* Get item to return */
        !           558:        item = (void *)walk->e->item;
        !           559: 
        !           560:        /* Point at next item for next time */
        !           561:        walk->e = walk->e->next;
        !           562: 
        !           563:        /* Return item */
        !           564:        return (item);
        !           565: }
        !           566: 
        !           567: /**********************************************************************
        !           568:                        DEFAULT CALLBACKS
        !           569: **********************************************************************/
        !           570: 
        !           571: static int
        !           572: ghash_default_equal(struct ghash *g, const void *item1, const void *item2)
        !           573: {
        !           574:        return (item1 == item2);
        !           575: }
        !           576: 
        !           577: static u_int32_t
        !           578: ghash_default_hash(struct ghash *g, const void *item)
        !           579: {
        !           580:        return ((u_int32_t)(u_long)item);
        !           581: }
        !           582: 
        !           583: static void
        !           584: ghash_default_add(struct ghash *g, void *item)
        !           585: {
        !           586:        return;
        !           587: }
        !           588: 
        !           589: static void
        !           590: ghash_default_del(struct ghash *g, void *item)
        !           591: {
        !           592:        return;
        !           593: }
        !           594: 
        !           595: /**********************************************************************
        !           596:                        HELPER METHODS
        !           597: **********************************************************************/
        !           598: 
        !           599: /*
        !           600:  * Resize the hash bucket array.
        !           601:  */
        !           602: static void
        !           603: ghash_rehash(struct ghash *g, u_int new_nbuckets)
        !           604: {
        !           605:        struct gent **new_buckets;
        !           606:        u_int i;
        !           607: 
        !           608:        /* Get new bucket array */
        !           609:        assert(new_nbuckets > 0);
        !           610:        if ((new_buckets = MALLOC(g->mtype.buckets,
        !           611:            new_nbuckets * sizeof(*new_buckets))) == NULL)
        !           612:                return;                                 /* can't do it */
        !           613:        memset(new_buckets, 0, new_nbuckets * sizeof(*new_buckets));
        !           614: 
        !           615:        /* Move all entries over to new array */
        !           616:        for (i = 0; i < g->nbuckets; i++) {
        !           617:                while (g->buckets[i] != NULL) {
        !           618:                        struct gent *const e = g->buckets[i];
        !           619:                        const u_int new_bucket
        !           620:                            = (*g->hash)(g, e->item) % new_nbuckets;
        !           621: 
        !           622:                        g->buckets[i] = e->next;
        !           623:                        e->next = new_buckets[new_bucket];
        !           624:                        new_buckets[new_bucket] = e;
        !           625:                }
        !           626:        }
        !           627:        g->nbuckets = new_nbuckets;
        !           628:        FREE(g->mtype.buckets, g->buckets);
        !           629:        g->buckets = new_buckets;
        !           630: 
        !           631:        /* Update new upper and lower size limits */
        !           632:        UPDATE_SIZES(g);
        !           633: }
        !           634: 
        !           635: #ifdef GHASH_TEST
        !           636: 
        !           637: /**********************************************************************
        !           638:                        TEST ROUTINE
        !           639: **********************************************************************/
        !           640: 
        !           641: #define MAX_ARGS               32
        !           642: #define WS                     " \t\r\n\v\f"
        !           643: 
        !           644: static ghash_equal_t   ghash_test_equal;
        !           645: static ghash_hash_t    ghash_test_hash;
        !           646: static ghash_add_t     ghash_test_add;
        !           647: static ghash_del_t     ghash_test_del;
        !           648: 
        !           649: static int     ghash_test_sort(const void *p1, const void *p2);
        !           650: 
        !           651: int
        !           652: main(int ac, char **av)
        !           653: {
        !           654:        char buf[1024];
        !           655:        struct ghash *g;
        !           656: 
        !           657:        if ((g = ghash_create(NULL, 3, 0, "ghash", ghash_test_hash,
        !           658:            ghash_test_equal, ghash_test_add, ghash_test_del)) == NULL)
        !           659:                err(1, "ghash_create");
        !           660: 
        !           661:        while (1) {
        !           662:                char *args[MAX_ARGS];
        !           663:                char *tokctx;
        !           664:                char *s;
        !           665: 
        !           666:                /* Prompt */
        !           667:                printf("Current size is %d items (%d buckets)\n",
        !           668:                    ghash_size(g), g->nbuckets);
        !           669: getline:
        !           670:                printf("> ");
        !           671:                if (fgets(buf, sizeof(buf), stdin) == NULL)
        !           672:                        break;
        !           673: 
        !           674:                /* Parse line */
        !           675:                for (ac = 0, s = strtok_r(buf, WS, &tokctx);
        !           676:                    ac < MAX_ARGS && s != NULL;
        !           677:                    ac++, s = strtok_r(NULL, WS, &tokctx)) {
        !           678:                        if ((args[ac] = STRDUP(NULL, s)) == NULL)
        !           679:                                err(1, "strdup");
        !           680:                }
        !           681:                if (ac == 0)
        !           682:                        goto getline;
        !           683: 
        !           684:                /* Do cmd */
        !           685:                if (strcmp(args[0], "put") == 0) {
        !           686:                        if (ac != 2)
        !           687:                                err(1, "usage: put <token>");
        !           688:                        printf("Putting \"%s\"...\n", args[1]);
        !           689:                        if (ghash_put(g, args[1]) == -1)
        !           690:                                err(1, "ghash_put");
        !           691:                        printf("Done\n");
        !           692:                } else if (strcmp(args[0], "get") == 0) {
        !           693:                        const char *s;
        !           694: 
        !           695:                        if (ac != 2)
        !           696:                                err(1, "usage: get <token>");
        !           697:                        if ((s = ghash_get(g, args[1])) == NULL)
        !           698:                                printf("\"%s\" was not found\n", args[1]);
        !           699:                        else
        !           700:                                printf("Found \"%s\"\n", s);
        !           701:                } else if (strcmp(args[0], "del") == 0) {
        !           702:                        int rtn;
        !           703: 
        !           704:                        if (ac != 2)
        !           705:                                err(1, "usage: del <token>");
        !           706:                        if ((rtn = ghash_remove(g, args[1])) == -1)
        !           707:                                err(1, "ghash_remove");
        !           708:                        if (rtn)
        !           709:                                printf("Removed \"%s\"\n", args[1]);
        !           710:                        else
        !           711:                                printf("\"%s\" was not found\n", args[1]);
        !           712:                } else if (strcmp(args[0], "dump") == 0) {
        !           713:                        struct ghash_iter *iter;
        !           714: 
        !           715:                        if ((iter = ghash_iter_create(g)) == NULL)
        !           716:                                err(1, "ghash_iter_create");
        !           717:                        printf("Iterating contents...\n");
        !           718:                        while (ghash_iter_has_next(iter)) {
        !           719:                                const char *s = ghash_iter_next(iter);
        !           720: 
        !           721:                                if (s == NULL)
        !           722:                                        err(1, "ghash_iter_next");
        !           723:                                printf("\t\"%s\"\n", s);
        !           724:                        }
        !           725:                        ghash_iter_destroy(&iter);
        !           726:                        printf("Done\n");
        !           727:                } else if (strcmp(args[0], "sort") == 0) {
        !           728:                        void **list;
        !           729:                        int i, num;
        !           730: 
        !           731:                        if ((num = ghash_dump(g, &list, TYPED_MEM_TEMP)) == -1)
        !           732:                                err(1, "ghash_get_sorted");
        !           733:                        printf("Sorting contents...\n");
        !           734:                        qsort(list, num, sizeof(*list), ghash_test_sort);
        !           735:                        for (i = 0; i < num; i++)
        !           736:                                printf("\t\"%s\"\n", (const char *)list[i]);
        !           737:                        FREE(TYPED_MEM_TEMP, list);
        !           738:                        printf("Done\n");
        !           739:                } else {
        !           740:                        printf("Commands:\n"
        !           741:                        "\tget <token>\n"
        !           742:                        "\tput <token>\n"
        !           743:                        "\tdel <token>\n"
        !           744:                        "\tdump\n"
        !           745:                        "\tsort\n");
        !           746:                }
        !           747:        }
        !           748:        if (ferror(stdin))
        !           749:                err(1, "stdin");
        !           750:        printf("\n");
        !           751:        ghash_destroy(&g);
        !           752:        typed_mem_dump(stdout);
        !           753:        return (0);
        !           754: }
        !           755: 
        !           756: static int
        !           757: ghash_test_equal(struct ghash *g, const void *item1, const void *item2)
        !           758: {
        !           759:        return (strcmp((const char *)item1, (const char *)item2) == 0);
        !           760: }
        !           761: 
        !           762: static int
        !           763: ghash_test_sort(const void *p1, const void *p2)
        !           764: {
        !           765:        const char *s1 = *((const char **)p1);
        !           766:        const char *s2 = *((const char **)p2);
        !           767: 
        !           768:        return (strcmp(s1, s2));
        !           769: }
        !           770: 
        !           771: static u_int32_t
        !           772: ghash_test_hash(struct ghash *g, const void *item)
        !           773: {
        !           774:        const char *s = item;
        !           775:        u_int32_t hash;
        !           776: 
        !           777:        for (hash = 0; *s != '\0'; s++)
        !           778:                hash = (hash * 31) + (u_char)*s;
        !           779:        return (hash);
        !           780: }
        !           781: 
        !           782: static void
        !           783: ghash_test_add(struct ghash *g, void *item)
        !           784: {
        !           785:        printf("-> adding \"%s\"\n", (char *)item);
        !           786: }
        !           787: 
        !           788: static void
        !           789: ghash_test_del(struct ghash *g, void *item)
        !           790: {
        !           791:        printf("-> deleting \"%s\"\n", (char *)item);
        !           792: }
        !           793: 
        !           794: #endif /* GHASH_TEST */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>