Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
      3  *
      4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
      5  *                                  and others.
      6  *
      7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
      8  *
      9  * You may distribute under the terms of the GNU General Public License as
     10  * specified in the README file that comes with the CVS source distribution.
     11  *
     12  * Polk's hash list manager.  So cool.
     13  */
     14 #include <sys/cdefs.h>
     15 __RCSID("$NetBSD: hash.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
     16 
     17 #include "cvs.h"
     18 
     19 /* Global caches.  The idea is that we maintain a linked list of "free"d
     20    nodes or lists, and get new items from there.  It has been suggested
     21    to use an obstack instead, but off the top of my head, I'm not sure
     22    that would gain enough to be worth worrying about.  */
     23 static List *listcache = NULL;
     24 static Node *nodecache = NULL;
     25 
     26 static void freenode_mem (Node * p);
     27 
     28 /* hash function */
     29 static int
     30 hashp (const char *key)
     31 {
     32     unsigned int h = 0;
     33     unsigned int g;
     34 
     35     assert(key != NULL);
     36 
     37     while (*key != 0)
     38     {
     39 	unsigned int c = *key++;
     40 	/* The FOLD_FN_CHAR is so that findnode_fn works.  */
     41 	h = (h << 4) + FOLD_FN_CHAR (c);
     42 	if ((g = h & 0xf0000000) != 0)
     43 	    h = (h ^ (g >> 24)) ^ g;
     44     }
     45 
     46     return h % HASHSIZE;
     47 }
     48 
     49 
     50 
     51 /*
     52  * create a new list (or get an old one from the cache)
     53  */
     54 List *
     55 getlist (void)
     56 {
     57     int i;
     58     List *list;
     59     Node *node;
     60 
     61     if (listcache != NULL)
     62     {
     63 	/* get a list from the cache and clear it */
     64 	list = listcache;
     65 	listcache = listcache->next;
     66 	list->next = NULL;
     67 	for (i = 0; i < HASHSIZE; i++)
     68 	    list->hasharray[i] = NULL;
     69     }
     70     else
     71     {
     72 	/* make a new list from scratch */
     73 	list = xmalloc (sizeof (List));
     74 	memset (list, 0, sizeof (List));
     75 	node = getnode ();
     76 	list->list = node;
     77 	node->type = HEADER;
     78 	node->next = node->prev = node;
     79     }
     80     return list;
     81 }
     82 
     83 
     84 
     85 /*
     86  * Free up a list.  For accessing globals which might be accessed via interrupt
     87  * handlers, it can be assumed that the first action of this function will be
     88  * to set the *listp to NULL.
     89  */
     90 void
     91 dellist (List **listp)
     92 {
     93     int i;
     94     Node *p;
     95     List *tmp;
     96 
     97     if (*listp == NULL)
     98 	return;
     99 
    100     tmp = *listp;
    101     *listp = NULL;
    102 
    103     p = tmp->list;
    104 
    105     /* free each node in the list (except header) */
    106     while (p->next != p)
    107 	delnode (p->next);
    108 
    109     /* free any list-private data, without freeing the actual header */
    110     freenode_mem (p);
    111 
    112     /* free up the header nodes for hash lists (if any) */
    113     for (i = 0; i < HASHSIZE; i++)
    114     {
    115 	if ((p = tmp->hasharray[i]) != NULL)
    116 	{
    117 	    /* put the nodes into the cache */
    118 #ifndef NOCACHE
    119 	    p->type = NT_UNKNOWN;
    120 	    p->next = nodecache;
    121 	    nodecache = p;
    122 #else
    123 	    /* If NOCACHE is defined we turn off the cache.  This can make
    124 	       it easier to tools to determine where items were allocated
    125 	       and freed, for tracking down memory leaks and the like.  */
    126 	    free (p);
    127 #endif
    128 	}
    129     }
    130 
    131     /* put it on the cache */
    132 #ifndef NOCACHE
    133     tmp->next = listcache;
    134     listcache = tmp;
    135 #else
    136     free (tmp->list);
    137     free (tmp);
    138 #endif
    139 }
    140 
    141 
    142 
    143 /*
    144  * remove a node from it's list (maybe hash list too)
    145  */
    146 void
    147 removenode (Node *p)
    148 {
    149     if (!p) return;
    150 
    151     /* take it out of the list */
    152     p->next->prev = p->prev;
    153     p->prev->next = p->next;
    154 
    155     /* if it was hashed, remove it from there too */
    156     if (p->hashnext)
    157     {
    158 	p->hashnext->hashprev = p->hashprev;
    159 	p->hashprev->hashnext = p->hashnext;
    160     }
    161 }
    162 
    163 
    164 
    165 void
    166 mergelists (List *dest, List **src)
    167 {
    168     Node *head, *p, *n;
    169 
    170     head = (*src)->list;
    171     for (p = head->next; p != head; p = n)
    172     {
    173 	n = p->next;
    174 	removenode (p);
    175 	addnode (dest, p);
    176     }
    177     dellist (src);
    178 }
    179 
    180 
    181 
    182 /*
    183  * get a new list node
    184  */
    185 Node *
    186 getnode (void)
    187 {
    188     Node *p;
    189 
    190     if (nodecache != NULL)
    191     {
    192 	/* get one from the cache */
    193 	p = nodecache;
    194 	nodecache = p->next;
    195     }
    196     else
    197     {
    198 	/* make a new one */
    199 	p = xmalloc (sizeof (Node));
    200     }
    201 
    202     /* always make it clean */
    203     memset (p, 0, sizeof (Node));
    204     p->type = NT_UNKNOWN;
    205 
    206     return p;
    207 }
    208 
    209 
    210 
    211 /*
    212  * remove a node from it's list (maybe hash list too) and free it
    213  */
    214 void
    215 delnode (Node *p)
    216 {
    217     if (!p) return;
    218     /* remove it */
    219     removenode (p);
    220     /* free up the storage */
    221     freenode (p);
    222 }
    223 
    224 
    225 
    226 /*
    227  * free up the storage associated with a node
    228  */
    229 static void
    230 freenode_mem (Node *p)
    231 {
    232     if (p->delproc != NULL)
    233 	p->delproc (p);			/* call the specified delproc */
    234     else
    235     {
    236 	if (p->data != NULL)		/* otherwise free() it if necessary */
    237 	    free (p->data);
    238     }
    239     if (p->key != NULL)			/* free the key if necessary */
    240 	free (p->key);
    241 
    242     /* to be safe, re-initialize these */
    243     p->key = p->data = NULL;
    244     p->delproc = NULL;
    245 }
    246 
    247 
    248 
    249 /*
    250  * free up the storage associated with a node and recycle it
    251  */
    252 void
    253 freenode (Node *p)
    254 {
    255     /* first free the memory */
    256     freenode_mem (p);
    257 
    258     /* then put it in the cache */
    259 #ifndef NOCACHE
    260     p->type = NT_UNKNOWN;
    261     p->next = nodecache;
    262     nodecache = p;
    263 #else
    264     free (p);
    265 #endif
    266 }
    267 
    268 
    269 
    270 /*
    271  * Link item P into list LIST before item MARKER.  If P->KEY is non-NULL and
    272  * that key is already in the hash table, return -1 without modifying any
    273  * parameter.
    274  *
    275  * return 0 on success
    276  */
    277 int
    278 insert_before (List *list, Node *marker, Node *p)
    279 {
    280     if (p->key != NULL)			/* hash it too? */
    281     {
    282 	int hashval;
    283 	Node *q;
    284 
    285 	hashval = hashp (p->key);
    286 	if (list->hasharray[hashval] == NULL)	/* make a header for list? */
    287 	{
    288 	    q = getnode ();
    289 	    q->type = HEADER;
    290 	    list->hasharray[hashval] = q->hashnext = q->hashprev = q;
    291 	}
    292 
    293 	/* put it into the hash list if it's not already there */
    294 	for (q = list->hasharray[hashval]->hashnext;
    295 	     q != list->hasharray[hashval]; q = q->hashnext)
    296 	{
    297 	    if (strcmp (p->key, q->key) == 0)
    298 		return -1;
    299 	}
    300 	q = list->hasharray[hashval];
    301 	p->hashprev = q->hashprev;
    302 	p->hashnext = q;
    303 	p->hashprev->hashnext = p;
    304 	q->hashprev = p;
    305     }
    306 
    307     p->next = marker;
    308     p->prev = marker->prev;
    309     marker->prev->next = p;
    310     marker->prev = p;
    311 
    312     return 0;
    313 }
    314 
    315 
    316 
    317 /*
    318  * insert item p at end of list "list" (maybe hash it too) if hashing and it
    319  * already exists, return -1 and don't actually put it in the list
    320  *
    321  * return 0 on success
    322  */
    323 int
    324 addnode (List *list, Node *p)
    325 {
    326   return insert_before (list, list->list, p);
    327 }
    328 
    329 
    330 
    331 /*
    332  * Like addnode, but insert p at the front of `list'.  This bogosity is
    333  * necessary to preserve last-to-first output order for some RCS functions.
    334  */
    335 int
    336 addnode_at_front (List *list, Node *p)
    337 {
    338   return insert_before (list, list->list->next, p);
    339 }
    340 
    341 
    342 
    343 /* Look up an entry in hash list table and return a pointer to the
    344  * node.  Return NULL if not found or if list is NULL.  Abort with a fatal
    345  * error for errors.
    346  */
    347 Node *
    348 findnode (List *list, const char *key)
    349 {
    350     Node *head, *p;
    351 
    352     if ((list == NULL))
    353 	return NULL;
    354 
    355     assert (key != NULL);
    356 
    357     head = list->hasharray[hashp (key)];
    358     if (head == NULL)
    359 	/* Not found.  */
    360 	return NULL;
    361 
    362     for (p = head->hashnext; p != head; p = p->hashnext)
    363 	if (strcmp (p->key, key) == 0)
    364 	    return p;
    365     return NULL;
    366 }
    367 
    368 
    369 
    370 /*
    371  * Like findnode, but for a filename.
    372  */
    373 Node *
    374 findnode_fn (List *list, const char *key)
    375 {
    376     Node *head, *p;
    377 
    378     /* This probably should be "assert (list != NULL)" (or if not we
    379        should document the current behavior), but only if we check all
    380        the callers to see if any are relying on this behavior.  */
    381     if (list == NULL)
    382 	return NULL;
    383 
    384     assert (key != NULL);
    385 
    386     head = list->hasharray[hashp (key)];
    387     if (head == NULL)
    388 	return NULL;
    389 
    390     for (p = head->hashnext; p != head; p = p->hashnext)
    391 	if (fncmp (p->key, key) == 0)
    392 	    return p;
    393     return NULL;
    394 }
    395 
    396 
    397 
    398 /*
    399  * walk a list with a specific proc
    400  */
    401 int
    402 walklist (List *list, int (*proc) (Node *, void *), void *closure)
    403 {
    404     Node *head, *p;
    405     int err = 0;
    406 
    407 #ifdef HAVE_PRINTF_PTR
    408     TRACE (TRACE_FLOW, "walklist ( list=%p, proc=%p, closure=%p )",
    409 	   (void *)list, (void *)proc, (void *)closure);
    410 #else
    411     TRACE (TRACE_FLOW, "walklist ( list=%lx, proc=%lx, closure=%lx )",
    412 	   (unsigned long)list,(unsigned long) proc,
    413 	   (unsigned long)closure);
    414 #endif
    415 
    416     if (list == NULL)
    417 	return 0;
    418 
    419     head = list->list;
    420     for (p = head->next; p != head; p = p->next)
    421 	err += proc (p, closure);
    422     return err;
    423 }
    424 
    425 
    426 
    427 int
    428 list_isempty (List *list)
    429 {
    430     return list == NULL || list->list->next == list->list;
    431 }
    432 
    433 
    434 
    435 static int (*client_comp) (const Node *, const Node *);
    436 
    437 static int
    438 qsort_comp (const void *elem1, const void *elem2)
    439 {
    440     Node **node1 = (Node **) elem1;
    441     Node **node2 = (Node **) elem2;
    442     return client_comp (*node1, *node2);
    443 }
    444 
    445 
    446 
    447 /*
    448  * sort the elements of a list (in place)
    449  */
    450 void
    451 sortlist (List *list, int (*comp) (const Node *, const Node *))
    452 {
    453     Node *head, *remain, *p, **array;
    454     int i, n;
    455 
    456     if (list == NULL)
    457 	return;
    458 
    459     /* save the old first element of the list */
    460     head = list->list;
    461     remain = head->next;
    462 
    463     /* count the number of nodes in the list */
    464     n = 0;
    465     for (p = remain; p != head; p = p->next)
    466 	n++;
    467 
    468     /* allocate an array of nodes and populate it */
    469     array = xnmalloc (n, sizeof (Node *));
    470     i = 0;
    471     for (p = remain; p != head; p = p->next)
    472 	array[i++] = p;
    473 
    474     /* sort the array of nodes */
    475     client_comp = comp;
    476     qsort (array, n, sizeof(Node *), qsort_comp);
    477 
    478     /* rebuild the list from beginning to end */
    479     head->next = head->prev = head;
    480     for (i = 0; i < n; i++)
    481     {
    482 	p = array[i];
    483 	p->next = head;
    484 	p->prev = head->prev;
    485 	p->prev->next = p;
    486 	head->prev = p;
    487     }
    488 
    489     /* release the array of nodes */
    490     free (array);
    491 }
    492 
    493 
    494 
    495 /*
    496  * compare two files list node (for sort)
    497  */
    498 int
    499 fsortcmp (const Node *p, const Node *q)
    500 {
    501     return strcmp (p->key, q->key);
    502 }
    503 
    504 
    505 
    506 /* Debugging functions.  Quite useful to call from within gdb. */
    507 
    508 
    509 static char *
    510 nodetypestring (Ntype type)
    511 {
    512     switch (type) {
    513     case NT_UNKNOWN:	return "UNKNOWN";
    514     case HEADER:	return "HEADER";
    515     case ENTRIES:	return "ENTRIES";
    516     case FILES:		return "FILES";
    517     case LIST:		return "LIST";
    518     case RCSNODE:	return "RCSNODE";
    519     case RCSVERS:	return "RCSVERS";
    520     case DIRS:		return "DIRS";
    521     case UPDATE:	return "UPDATE";
    522     case LOCK:		return "LOCK";
    523     case NDBMNODE:	return "NDBMNODE";
    524     case FILEATTR:	return "FILEATTR";
    525     case VARIABLE:	return "VARIABLE";
    526     case RCSFIELD:	return "RCSFIELD";
    527     case RCSCMPFLD:	return "RCSCMPFLD";
    528     }
    529 
    530     return "<trash>";
    531 }
    532 
    533 
    534 
    535 static int
    536 printnode (Node *node, void *closure)
    537 {
    538     if (node == NULL)
    539     {
    540 	(void) printf("NULL node.\n");
    541 	return 0;
    542     }
    543 
    544 #ifdef HAVE_PRINTF_PTR
    545     (void) printf("Node at %p: type = %s, key = %p = \"%s\", data = %p, next = %p, prev = %p\n",
    546 	   (void *) node, nodetypestring(node->type),
    547 	   (void *) node->key, node->key, node->data,
    548 	   (void *) node->next, (void *) node->prev);
    549 #else
    550     (void) printf("Node at 0x%lx: type = %s, key = 0x%lx = \"%s\", data = 0x%lx, next = 0x%lx, prev = 0x%lx\n",
    551 	   (unsigned long) node, nodetypestring(node->type),
    552 	   (unsigned long) node->key, node->key, (unsigned long) node->data,
    553 	   (unsigned long) node->next, (unsigned long) node->prev);
    554 #endif
    555 
    556     return 0;
    557 }
    558 
    559 
    560 
    561 /* This is global, not static, so that its name is unique and to avoid
    562    compiler warnings about it not being used.  But it is not used by CVS;
    563    it exists so one can call it from a debugger.  */
    564 
    565 void
    566 printlist (List *list)
    567 {
    568     if (list == NULL)
    569     {
    570 	(void) printf("NULL list.\n");
    571 	return;
    572     }
    573 
    574 #ifdef HAVE_PRINTF_PTR
    575     (void) printf("List at %p: list = %p, HASHSIZE = %d, next = %p\n",
    576 	   (void *) list, (void *) list->list, HASHSIZE, (void *) list->next);
    577 #else
    578     (void) printf("List at 0x%lx: list = 0x%lx, HASHSIZE = %d, next = 0x%lx\n",
    579 	   (unsigned long) list, (unsigned long) list->list, HASHSIZE,
    580 	   (unsigned long) list->next);
    581 #endif
    582 
    583     (void) walklist(list, printnode, NULL);
    584 
    585     return;
    586 }
    587