hash.c revision 70a9da54eb200cd5c5ceafb72aff72c39021c94c
1/* 2 * hash.c: chained hash tables 3 * 4 * Reference: Your favorite introductory book on algorithms 5 * 6 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard. 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 13 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 14 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND 15 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. 16 * 17 * Author: breese@users.sourceforge.net 18 */ 19 20#include "libxml.h" 21 22#include <string.h> 23#include <libxml/hash.h> 24#include <libxml/xmlmemory.h> 25#include <libxml/parser.h> 26#include <libxml/xmlerror.h> 27 28#define MAX_HASH_LEN 8 29 30/* #define DEBUG_GROW */ 31 32/* 33 * A single entry in the hash table 34 */ 35typedef struct _xmlHashEntry xmlHashEntry; 36typedef xmlHashEntry *xmlHashEntryPtr; 37struct _xmlHashEntry { 38 struct _xmlHashEntry *next; 39 xmlChar *name; 40 xmlChar *name2; 41 xmlChar *name3; 42 void *payload; 43}; 44 45/* 46 * The entire hash table 47 */ 48struct _xmlHashTable { 49 struct _xmlHashEntry **table; 50 int size; 51 int nbElems; 52}; 53 54/* 55 * xmlHashComputeKey: 56 * Calculate the hash key 57 */ 58static unsigned long 59xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name, 60 const xmlChar *name2, const xmlChar *name3) { 61 unsigned long value = 0L; 62 char ch; 63 64 if (name != NULL) { 65 value += 30 * (*name); 66 while ((ch = *name++) != 0) { 67 /* value *= 31; */ 68 value += (unsigned long)ch; 69 } 70 } 71 if (name2 != NULL) { 72 while ((ch = *name2++) != 0) { 73 /* value *= 31; */ 74 value += (unsigned long)ch; 75 } 76 } 77 if (name3 != NULL) { 78 while ((ch = *name3++) != 0) { 79 /* value *= 31; */ 80 value += (unsigned long)ch; 81 } 82 } 83 return (value % table->size); 84} 85 86/** 87 * xmlHashCreate: 88 * @size: the size of the hash table 89 * 90 * Create a new xmlHashTablePtr. 91 * 92 * Returns the newly created object, or NULL if an error occured. 93 */ 94xmlHashTablePtr 95xmlHashCreate(int size) { 96 xmlHashTablePtr table; 97 98 if (size <= 0) 99 size = 256; 100 101 table = xmlMalloc(sizeof(xmlHashTable)); 102 if (table) { 103 table->size = size; 104 table->nbElems = 0; 105 table->table = xmlMalloc(size * sizeof(xmlHashEntry)); 106 if (table->table) { 107 memset(table->table, 0, size * sizeof(xmlHashEntry)); 108 return(table); 109 } 110 xmlFree(table); 111 } 112 return(NULL); 113} 114 115/** 116 * xmlHashGrow: 117 * @table: the hash table 118 * @size: the new size of the hash table 119 * 120 * resize the hash table 121 * 122 * Returns 0 in case of success, -1 in case of failure 123 */ 124static int 125xmlHashGrow(xmlHashTablePtr table, int size) { 126 unsigned long key; 127 int oldsize, i; 128 xmlHashEntryPtr iter, next; 129 struct _xmlHashEntry **oldtable; 130#ifdef DEBUG_GROW 131 unsigned long nbElem = 0; 132#endif 133 134 if (table == NULL) 135 return(-1); 136 if (size < 8) 137 return(-1); 138 if (size > 8 * 2048) 139 return(-1); 140 141 oldsize = table->size; 142 oldtable = table->table; 143 if (oldtable == NULL) 144 return(-1); 145 146 table->table = xmlMalloc(size * sizeof(xmlHashEntry)); 147 if (table->table == NULL) { 148 table->table = oldtable; 149 return(-1); 150 } 151 memset(table->table, 0, size * sizeof(xmlHashEntry)); 152 table->size = size; 153 154 for (i = 0; i < oldsize; i++) { 155 iter = oldtable[i]; 156 while (iter) { 157 next = iter->next; 158 159 /* 160 * put back the entry in the new table 161 */ 162 163 key = xmlHashComputeKey(table, iter->name, iter->name2, 164 iter->name3); 165 iter->next = table->table[key]; 166 table->table[key] = iter; 167 168#ifdef DEBUG_GROW 169 nbElem++; 170#endif 171 172 iter = next; 173 } 174 } 175 176 xmlFree(oldtable); 177 178#ifdef DEBUG_GROW 179 xmlGenericError(xmlGenericErrorContext, 180 "xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem); 181#endif 182 183 return(0); 184} 185 186/** 187 * xmlHashFree: 188 * @table: the hash table 189 * @f: the deallocator function for items in the hash 190 * 191 * Free the hash table and its contents. The userdata is 192 * deallocated with f if provided. 193 */ 194void 195xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) { 196 int i; 197 xmlHashEntryPtr iter; 198 xmlHashEntryPtr next; 199 200 if (table == NULL) 201 return; 202 if (table->table) { 203 for(i = 0; i < table->size; i++) { 204 iter = table->table[i]; 205 while (iter) { 206 next = iter->next; 207 if (f) 208 f(iter->payload, iter->name); 209 if (iter->name) 210 xmlFree(iter->name); 211 if (iter->name2) 212 xmlFree(iter->name2); 213 if (iter->name3) 214 xmlFree(iter->name3); 215 iter->payload = NULL; 216 xmlFree(iter); 217 iter = next; 218 } 219 table->table[i] = NULL; 220 } 221 xmlFree(table->table); 222 } 223 xmlFree(table); 224} 225 226/** 227 * xmlHashAddEntry: 228 * @table: the hash table 229 * @name: the name of the userdata 230 * @userdata: a pointer to the userdata 231 * 232 * Add the userdata to the hash table. This can later be retrieved 233 * by using the name. Duplicate names generate errors. 234 * 235 * Returns 0 the addition succeeded and -1 in case of error. 236 */ 237int 238xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) { 239 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata)); 240} 241 242/** 243 * xmlHashAddEntry2: 244 * @table: the hash table 245 * @name: the name of the userdata 246 * @name2: a second name of the userdata 247 * @userdata: a pointer to the userdata 248 * 249 * Add the userdata to the hash table. This can later be retrieved 250 * by using the (name, name2) tuple. Duplicate tuples generate errors. 251 * 252 * Returns 0 the addition succeeded and -1 in case of error. 253 */ 254int 255xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name, 256 const xmlChar *name2, void *userdata) { 257 return(xmlHashAddEntry3(table, name, name2, NULL, userdata)); 258} 259 260/** 261 * xmlHashUpdateEntry: 262 * @table: the hash table 263 * @name: the name of the userdata 264 * @userdata: a pointer to the userdata 265 * @f: the deallocator function for replaced item (if any) 266 * 267 * Add the userdata to the hash table. This can later be retrieved 268 * by using the name. Existing entry for this name will be removed 269 * and freed with @f if found. 270 * 271 * Returns 0 the addition succeeded and -1 in case of error. 272 */ 273int 274xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name, 275 void *userdata, xmlHashDeallocator f) { 276 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f)); 277} 278 279/** 280 * xmlHashUpdateEntry2: 281 * @table: the hash table 282 * @name: the name of the userdata 283 * @name2: a second name of the userdata 284 * @userdata: a pointer to the userdata 285 * @f: the deallocator function for replaced item (if any) 286 * 287 * Add the userdata to the hash table. This can later be retrieved 288 * by using the (name, name2) tuple. Existing entry for this tuple will 289 * be removed and freed with @f if found. 290 * 291 * Returns 0 the addition succeeded and -1 in case of error. 292 */ 293int 294xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name, 295 const xmlChar *name2, void *userdata, 296 xmlHashDeallocator f) { 297 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f)); 298} 299 300/** 301 * xmlHashLookup: 302 * @table: the hash table 303 * @name: the name of the userdata 304 * 305 * Find the userdata specified by the name. 306 * 307 * Returns the a pointer to the userdata 308 */ 309void * 310xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) { 311 return(xmlHashLookup3(table, name, NULL, NULL)); 312} 313 314/** 315 * xmlHashLookup2: 316 * @table: the hash table 317 * @name: the name of the userdata 318 * @name2: a second name of the userdata 319 * 320 * Find the userdata specified by the (name, name2) tuple. 321 * 322 * Returns the a pointer to the userdata 323 */ 324void * 325xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name, 326 const xmlChar *name2) { 327 return(xmlHashLookup3(table, name, name2, NULL)); 328} 329 330/** 331 * xmlHashAddEntry3: 332 * @table: the hash table 333 * @name: the name of the userdata 334 * @name2: a second name of the userdata 335 * @name3: a third name of the userdata 336 * @userdata: a pointer to the userdata 337 * 338 * Add the userdata to the hash table. This can later be retrieved 339 * by using the tuple (name, name2, name3). Duplicate entries generate 340 * errors. 341 * 342 * Returns 0 the addition succeeded and -1 in case of error. 343 */ 344int 345xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name, 346 const xmlChar *name2, const xmlChar *name3, 347 void *userdata) { 348 unsigned long key, len = 0; 349 xmlHashEntryPtr entry; 350 xmlHashEntryPtr insert; 351 352 if ((table == NULL) || name == NULL) 353 return(-1); 354 355 /* 356 * Check for duplicate and insertion location. 357 */ 358 key = xmlHashComputeKey(table, name, name2, name3); 359 if (table->table[key] == NULL) { 360 insert = NULL; 361 } else { 362 for (insert = table->table[key]; insert->next != NULL; 363 insert = insert->next) { 364 if ((xmlStrEqual(insert->name, name)) && 365 (xmlStrEqual(insert->name2, name2)) && 366 (xmlStrEqual(insert->name3, name3))) 367 return(-1); 368 len++; 369 } 370 if ((xmlStrEqual(insert->name, name)) && 371 (xmlStrEqual(insert->name2, name2)) && 372 (xmlStrEqual(insert->name3, name3))) 373 return(-1); 374 } 375 376 entry = xmlMalloc(sizeof(xmlHashEntry)); 377 if (entry == NULL) 378 return(-1); 379 entry->name = xmlStrdup(name); 380 entry->name2 = xmlStrdup(name2); 381 entry->name3 = xmlStrdup(name3); 382 entry->payload = userdata; 383 entry->next = NULL; 384 385 386 if (insert == NULL) { 387 table->table[key] = entry; 388 } else { 389 insert->next = entry; 390 } 391 table->nbElems++; 392 393 if (len > MAX_HASH_LEN) 394 xmlHashGrow(table, MAX_HASH_LEN * table->size); 395 396 return(0); 397} 398 399/** 400 * xmlHashUpdateEntry3: 401 * @table: the hash table 402 * @name: the name of the userdata 403 * @name2: a second name of the userdata 404 * @name3: a third name of the userdata 405 * @userdata: a pointer to the userdata 406 * @f: the deallocator function for replaced item (if any) 407 * 408 * Add the userdata to the hash table. This can later be retrieved 409 * by using the tuple (name, name2, name3). Existing entry for this tuple 410 * will be removed and freed with @f if found. 411 * 412 * Returns 0 the addition succeeded and -1 in case of error. 413 */ 414int 415xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name, 416 const xmlChar *name2, const xmlChar *name3, 417 void *userdata, xmlHashDeallocator f) { 418 unsigned long key; 419 xmlHashEntryPtr entry; 420 xmlHashEntryPtr insert; 421 422 if ((table == NULL) || name == NULL) 423 return(-1); 424 425 /* 426 * Check for duplicate and insertion location. 427 */ 428 key = xmlHashComputeKey(table, name, name2, name3); 429 if (table->table[key] == NULL) { 430 insert = NULL; 431 } else { 432 for (insert = table->table[key]; insert->next != NULL; 433 insert = insert->next) { 434 if ((xmlStrEqual(insert->name, name)) && 435 (xmlStrEqual(insert->name2, name2)) && 436 (xmlStrEqual(insert->name3, name3))) { 437 if (f) 438 f(insert->payload, insert->name); 439 insert->payload = userdata; 440 return(0); 441 } 442 } 443 if ((xmlStrEqual(insert->name, name)) && 444 (xmlStrEqual(insert->name2, name2)) && 445 (xmlStrEqual(insert->name3, name3))) { 446 if (f) 447 f(insert->payload, insert->name); 448 insert->payload = userdata; 449 return(0); 450 } 451 } 452 453 entry = xmlMalloc(sizeof(xmlHashEntry)); 454 if (entry == NULL) 455 return(-1); 456 entry->name = xmlStrdup(name); 457 entry->name2 = xmlStrdup(name2); 458 entry->name3 = xmlStrdup(name3); 459 entry->payload = userdata; 460 entry->next = NULL; 461 table->nbElems++; 462 463 464 if (insert == NULL) { 465 table->table[key] = entry; 466 } else { 467 insert->next = entry; 468 } 469 return(0); 470} 471 472/** 473 * xmlHashLookup: 474 * @table: the hash table 475 * @name: the name of the userdata 476 * @name2: a second name of the userdata 477 * @name3: a third name of the userdata 478 * 479 * Find the userdata specified by the (name, name2, name3) tuple. 480 * 481 * Returns the a pointer to the userdata 482 */ 483void * 484xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name, 485 const xmlChar *name2, const xmlChar *name3) { 486 unsigned long key; 487 xmlHashEntryPtr entry; 488 489 if (table == NULL) 490 return(NULL); 491 if (name == NULL) 492 return(NULL); 493 key = xmlHashComputeKey(table, name, name2, name3); 494 for (entry = table->table[key]; entry != NULL; entry = entry->next) { 495 if ((xmlStrEqual(entry->name, name)) && 496 (xmlStrEqual(entry->name2, name2)) && 497 (xmlStrEqual(entry->name3, name3))) 498 return(entry->payload); 499 } 500 return(NULL); 501} 502 503/** 504 * xmlHashScan: 505 * @table: the hash table 506 * @f: the scanner function for items in the hash 507 * @data: extra data passed to f 508 * 509 * Scan the hash table and applied f to each value. 510 */ 511void 512xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) { 513 int i; 514 xmlHashEntryPtr iter; 515 xmlHashEntryPtr next; 516 517 if (table == NULL) 518 return; 519 if (f == NULL) 520 return; 521 522 if (table->table) { 523 for(i = 0; i < table->size; i++) { 524 iter = table->table[i]; 525 while (iter) { 526 next = iter->next; 527 if (f) 528 f(iter->payload, data, iter->name); 529 iter = next; 530 } 531 } 532 } 533} 534 535/** 536 * xmlHashScan3: 537 * @table: the hash table 538 * @name: the name of the userdata or NULL 539 * @name2: a second name of the userdata or NULL 540 * @name3: a third name of the userdata or NULL 541 * @f: the scanner function for items in the hash 542 * @data: extra data passed to f 543 * 544 * Scan the hash table and applied f to each value matching 545 * (name, name2, name3) tuple. If one of the names is null, 546 * the comparison is considered to match. 547 */ 548void 549xmlHashScan3(xmlHashTablePtr table, const xmlChar *name, 550 const xmlChar *name2, const xmlChar *name3, 551 xmlHashScanner f, void *data) { 552 int i; 553 xmlHashEntryPtr iter; 554 xmlHashEntryPtr next; 555 556 if (table == NULL) 557 return; 558 if (f == NULL) 559 return; 560 561 if (table->table) { 562 for(i = 0; i < table->size; i++) { 563 iter = table->table[i]; 564 while (iter) { 565 next = iter->next; 566 if (((name == NULL) || (xmlStrEqual(name, iter->name))) && 567 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) && 568 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3)))) { 569 f(iter->payload, data, iter->name); 570 } 571 iter = next; 572 } 573 } 574 } 575} 576 577/** 578 * xmlHashCopy: 579 * @table: the hash table 580 * @f: the copier function for items in the hash 581 * 582 * Scan the hash table and applied f to each value. 583 * 584 * Returns the new table or NULL in case of error. 585 */ 586xmlHashTablePtr 587xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) { 588 int i; 589 xmlHashEntryPtr iter; 590 xmlHashEntryPtr next; 591 xmlHashTablePtr ret; 592 593 if (table == NULL) 594 return(NULL); 595 if (f == NULL) 596 return(NULL); 597 598 ret = xmlHashCreate(table->size); 599 if (table->table) { 600 for(i = 0; i < table->size; i++) { 601 iter = table->table[i]; 602 while (iter) { 603 next = iter->next; 604 xmlHashAddEntry3(ret, iter->name, iter->name2, 605 iter->name3, f(iter->payload, iter->name)); 606 iter = next; 607 } 608 } 609 } 610 ret->nbElems = table->nbElems; 611 return(ret); 612} 613 614/** 615 * xmlHashSize: 616 * @table: the hash table 617 * 618 * Returns the number of elements in the hash table or 619 * -1 in case of error 620 */ 621int 622xmlHashSize(xmlHashTablePtr table) { 623 if (table == NULL) 624 return(-1); 625 return(table->nbElems); 626} 627 628/** 629 * @table: the hash table 630 * @name: the name of the userdata 631 * @f: the deallocator function for removed item (if any) 632 * 633 * Find the userdata specified by the (name, name2, name3) tuple and remove 634 * it from the hash table. Existing userdata for this tuple will be removed 635 * and freed with @f. 636 * 637 * Returns 0 if the removal succeeded and -1 in case of error or not found. 638 */ 639int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name, 640 xmlHashDeallocator f) { 641 return(xmlHashRemoveEntry3(table, name, NULL, NULL, f)); 642} 643 644/** 645 * @table: the hash table 646 * @name: the name of the userdata 647 * @name2: a second name of the userdata 648 * @f: the deallocator function for removed item (if any) 649 * 650 * Find the userdata specified by the (name, name2, name3) tuple and remove 651 * it from the hash table. Existing userdata for this tuple will be removed 652 * and freed with @f. 653 * 654 * Returns 0 if the removal succeeded and -1 in case of error or not found. 655 */ 656int xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name, 657 const xmlChar *name2, xmlHashDeallocator f) { 658 return(xmlHashRemoveEntry3(table, name, name2, NULL, f)); 659} 660 661/** 662 * @table: the hash table 663 * @name: the name of the userdata 664 * @name2: a second name of the userdata 665 * @name3: a third name of the userdata 666 * @f: the deallocator function for removed item (if any) 667 * 668 * Find the userdata specified by the (name, name2, name3) tuple and remove 669 * it from the hash table. Existing userdata for this tuple will be removed 670 * and freed with @f. 671 * 672 * Returns 0 if the removal succeeded and -1 in case of error or not found. 673 */ 674int xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name, 675 const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) { 676 unsigned long key; 677 xmlHashEntryPtr entry; 678 xmlHashEntryPtr prev = NULL; 679 680 if (table == NULL || name == NULL) 681 return(-1); 682 683 key = xmlHashComputeKey(table, name, name2, name3); 684 if (table->table[key] == NULL) { 685 return(-1); 686 } else { 687 for (entry = table->table[key]; entry != NULL; entry = entry->next) { 688 if (xmlStrEqual(entry->name, name) && 689 xmlStrEqual(entry->name2, name2) && 690 xmlStrEqual(entry->name3, name3)) { 691 if(f) 692 f(entry->payload, entry->name); 693 entry->payload = NULL; 694 if(entry->name) 695 xmlFree(entry->name); 696 if(entry->name2) 697 xmlFree(entry->name2); 698 if(entry->name3) 699 xmlFree(entry->name3); 700 if(prev) 701 prev->next = entry->next; 702 else 703 table->table[key] = entry->next; 704 xmlFree(entry); 705 table->nbElems--; 706 return(0); 707 } 708 prev = entry; 709 } 710 return(-1); 711 } 712} 713 714