File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / util / ghash.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (13 years, 1 month ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel

    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>