Annotation of embedaddon/mpd/src/contrib/libpdel/util/ghash.c, revision 1.1.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>