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