Reactos
at master 1214 lines 32 kB view raw
1/* 2 * hash.c: hash tables 3 * 4 * Hash table with open addressing, linear probing and 5 * Robin Hood reordering. 6 * 7 * See Copyright for the status of this software. 8 */ 9 10#define IN_LIBXML 11#include "libxml.h" 12 13#include <string.h> 14#include <limits.h> 15 16#include <libxml/parser.h> 17#include <libxml/hash.h> 18#include <libxml/dict.h> 19#include <libxml/xmlmemory.h> 20#include <libxml/xmlstring.h> 21 22#include "private/dict.h" 23 24#ifndef SIZE_MAX 25 #define SIZE_MAX ((size_t) -1) 26#endif 27 28#define MAX_FILL_NUM 7 29#define MAX_FILL_DENOM 8 30#define MIN_HASH_SIZE 8 31#define MAX_HASH_SIZE (1u << 31) 32 33/* 34 * A single entry in the hash table 35 */ 36typedef struct { 37 unsigned hashValue; /* 0 means unoccupied, occupied entries have the 38 * MAX_HASH_SIZE bit set to 1 */ 39 xmlChar *key; 40 xmlChar *key2; /* TODO: Don't allocate possibly empty keys */ 41 xmlChar *key3; 42 void *payload; 43} xmlHashEntry; 44 45/* 46 * The entire hash table 47 */ 48struct _xmlHashTable { 49 xmlHashEntry *table; 50 unsigned size; /* power of two */ 51 unsigned nbElems; 52 xmlDictPtr dict; 53 unsigned randomSeed; 54}; 55 56static int 57xmlHashGrow(xmlHashTablePtr hash, unsigned size); 58 59ATTRIBUTE_NO_SANITIZE_INTEGER 60static unsigned 61xmlHashValue(unsigned seed, const xmlChar *key, const xmlChar *key2, 62 const xmlChar *key3, size_t *lengths) { 63 unsigned h1, h2; 64 size_t i; 65 66 HASH_INIT(h1, h2, seed); 67 68 for (i = 0; key[i] != 0; i++) { 69 HASH_UPDATE(h1, h2, key[i]); 70 } 71 if (lengths) 72 lengths[0] = i; 73 74 HASH_UPDATE(h1, h2, 0); 75 76 if (key2 != NULL) { 77 for (i = 0; key2[i] != 0; i++) { 78 HASH_UPDATE(h1, h2, key2[i]); 79 } 80 if (lengths) 81 lengths[1] = i; 82 } 83 84 HASH_UPDATE(h1, h2, 0); 85 86 if (key3 != NULL) { 87 for (i = 0; key3[i] != 0; i++) { 88 HASH_UPDATE(h1, h2, key3[i]); 89 } 90 if (lengths) 91 lengths[2] = i; 92 } 93 94 HASH_FINISH(h1, h2); 95 96 return(h2); 97} 98 99ATTRIBUTE_NO_SANITIZE_INTEGER 100static unsigned 101xmlHashQNameValue(unsigned seed, 102 const xmlChar *prefix, const xmlChar *name, 103 const xmlChar *prefix2, const xmlChar *name2, 104 const xmlChar *prefix3, const xmlChar *name3) { 105 unsigned h1, h2, ch; 106 107 HASH_INIT(h1, h2, seed); 108 109 if (prefix != NULL) { 110 while ((ch = *prefix++) != 0) { 111 HASH_UPDATE(h1, h2, ch); 112 } 113 HASH_UPDATE(h1, h2, ':'); 114 } 115 if (name != NULL) { 116 while ((ch = *name++) != 0) { 117 HASH_UPDATE(h1, h2, ch); 118 } 119 } 120 HASH_UPDATE(h1, h2, 0); 121 if (prefix2 != NULL) { 122 while ((ch = *prefix2++) != 0) { 123 HASH_UPDATE(h1, h2, ch); 124 } 125 HASH_UPDATE(h1, h2, ':'); 126 } 127 if (name2 != NULL) { 128 while ((ch = *name2++) != 0) { 129 HASH_UPDATE(h1, h2, ch); 130 } 131 } 132 HASH_UPDATE(h1, h2, 0); 133 if (prefix3 != NULL) { 134 while ((ch = *prefix3++) != 0) { 135 HASH_UPDATE(h1, h2, ch); 136 } 137 HASH_UPDATE(h1, h2, ':'); 138 } 139 if (name3 != NULL) { 140 while ((ch = *name3++) != 0) { 141 HASH_UPDATE(h1, h2, ch); 142 } 143 } 144 145 HASH_FINISH(h1, h2); 146 147 return(h2); 148} 149 150/** 151 * xmlHashCreate: 152 * @size: initial size of the hash table 153 * 154 * Create a new hash table. Set size to zero if the number of entries 155 * can't be estimated. 156 * 157 * Returns the newly created object, or NULL if a memory allocation failed. 158 */ 159xmlHashTablePtr 160xmlHashCreate(int size) { 161 xmlHashTablePtr hash; 162 163 xmlInitParser(); 164 165 hash = xmlMalloc(sizeof(*hash)); 166 if (hash == NULL) 167 return(NULL); 168 hash->dict = NULL; 169 hash->size = 0; 170 hash->table = NULL; 171 hash->nbElems = 0; 172 hash->randomSeed = xmlRandom(); 173#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 174 hash->randomSeed = 0; 175#endif 176 177 /* 178 * Unless a larger size is passed, the backing table is created 179 * lazily with MIN_HASH_SIZE capacity. In practice, there are many 180 * hash tables which are never filled. 181 */ 182 if (size > MIN_HASH_SIZE) { 183 unsigned newSize = MIN_HASH_SIZE * 2; 184 185 while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE)) 186 newSize *= 2; 187 188 if (xmlHashGrow(hash, newSize) != 0) { 189 xmlFree(hash); 190 return(NULL); 191 } 192 } 193 194 return(hash); 195} 196 197/** 198 * xmlHashCreateDict: 199 * @size: the size of the hash table 200 * @dict: a dictionary to use for the hash 201 * 202 * Create a new hash table backed by a dictionary. This can reduce 203 * resource usage considerably if most keys passed to API functions 204 * originate from this dictionary. 205 * 206 * Returns the newly created object, or NULL if a memory allocation failed. 207 */ 208xmlHashTablePtr 209xmlHashCreateDict(int size, xmlDictPtr dict) { 210 xmlHashTablePtr hash; 211 212 hash = xmlHashCreate(size); 213 if (hash != NULL) { 214 hash->dict = dict; 215 xmlDictReference(dict); 216 } 217 return(hash); 218} 219 220/** 221 * xmlHashFree: 222 * @hash: hash table 223 * @dealloc: deallocator function or NULL 224 * 225 * Free the hash and its contents. The payload is deallocated with 226 * @dealloc if provided. 227 */ 228void 229xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) { 230 if (hash == NULL) 231 return; 232 233 if (hash->table) { 234 const xmlHashEntry *end = &hash->table[hash->size]; 235 const xmlHashEntry *entry; 236 237 for (entry = hash->table; entry < end; entry++) { 238 if (entry->hashValue == 0) 239 continue; 240 if ((dealloc != NULL) && (entry->payload != NULL)) 241 dealloc(entry->payload, entry->key); 242 if (hash->dict == NULL) { 243 if (entry->key) 244 xmlFree(entry->key); 245 if (entry->key2) 246 xmlFree(entry->key2); 247 if (entry->key3) 248 xmlFree(entry->key3); 249 } 250 } 251 252 xmlFree(hash->table); 253 } 254 255 if (hash->dict) 256 xmlDictFree(hash->dict); 257 258 xmlFree(hash); 259} 260 261/** 262 * xmlFastStrEqual: 263 * @s1: string 264 * @s2: string 265 * 266 * Compare two strings for equality, allowing NULL values. 267 */ 268static int 269xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) { 270 if (s1 == NULL) 271 return(s2 == NULL); 272 else 273 return((s2 != NULL) && 274 (strcmp((const char *) s1, (const char *) s2) == 0)); 275} 276 277/** 278 * xmlHashFindEntry: 279 * @hash: hash table, non-NULL, size > 0 280 * @key: first string key, non-NULL 281 * @key2: second string key 282 * @key3: third string key 283 * @hashValue: valid hash value of keys 284 * @pfound: result of search 285 * 286 * Try to find a matching hash table entry. If an entry was found, set 287 * @found to 1 and return the entry. Otherwise, set @found to 0 and return 288 * the location where a new entry should be inserted. 289 */ 290ATTRIBUTE_NO_SANITIZE_INTEGER 291static xmlHashEntry * 292xmlHashFindEntry(const xmlHashTable *hash, const xmlChar *key, 293 const xmlChar *key2, const xmlChar *key3, 294 unsigned hashValue, int *pfound) { 295 xmlHashEntry *entry; 296 unsigned mask, pos, displ; 297 int found = 0; 298 299 mask = hash->size - 1; 300 pos = hashValue & mask; 301 entry = &hash->table[pos]; 302 303 if (entry->hashValue != 0) { 304 /* 305 * Robin hood hashing: abort if the displacement of the entry 306 * is smaller than the displacement of the key we look for. 307 * This also stops at the correct position when inserting. 308 */ 309 displ = 0; 310 hashValue |= MAX_HASH_SIZE; 311 312 do { 313 if (entry->hashValue == hashValue) { 314 if (hash->dict) { 315 if ((entry->key == key) && 316 (entry->key2 == key2) && 317 (entry->key3 == key3)) { 318 found = 1; 319 break; 320 } 321 } 322 if ((strcmp((const char *) entry->key, 323 (const char *) key) == 0) && 324 (xmlFastStrEqual(entry->key2, key2)) && 325 (xmlFastStrEqual(entry->key3, key3))) { 326 found = 1; 327 break; 328 } 329 } 330 331 displ++; 332 pos++; 333 entry++; 334 if ((pos & mask) == 0) 335 entry = hash->table; 336 } while ((entry->hashValue != 0) && 337 (((pos - entry->hashValue) & mask) >= displ)); 338 } 339 340 *pfound = found; 341 return(entry); 342} 343 344/** 345 * xmlHashGrow: 346 * @hash: hash table 347 * @size: new size of the hash table 348 * 349 * Resize the hash table. 350 * 351 * Returns 0 in case of success, -1 if a memory allocation failed. 352 */ 353static int 354xmlHashGrow(xmlHashTablePtr hash, unsigned size) { 355 const xmlHashEntry *oldentry, *oldend, *end; 356 xmlHashEntry *table; 357 unsigned oldsize, i; 358 359 /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */ 360 if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0])) 361 return(-1); 362 table = xmlMalloc(size * sizeof(table[0])); 363 if (table == NULL) 364 return(-1); 365 memset(table, 0, size * sizeof(table[0])); 366 367 oldsize = hash->size; 368 if (oldsize == 0) 369 goto done; 370 371 oldend = &hash->table[oldsize]; 372 end = &table[size]; 373 374 /* 375 * Robin Hood sorting order is maintained if we 376 * 377 * - compute hash indices with modulo 378 * - resize by an integer factor 379 * - start to copy from the beginning of a probe sequence 380 */ 381 oldentry = hash->table; 382 while (oldentry->hashValue != 0) { 383 if (++oldentry >= oldend) 384 oldentry = hash->table; 385 } 386 387 for (i = 0; i < oldsize; i++) { 388 if (oldentry->hashValue != 0) { 389 xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)]; 390 391 while (entry->hashValue != 0) { 392 if (++entry >= end) 393 entry = table; 394 } 395 *entry = *oldentry; 396 } 397 398 if (++oldentry >= oldend) 399 oldentry = hash->table; 400 } 401 402 xmlFree(hash->table); 403 404done: 405 hash->table = table; 406 hash->size = size; 407 408 return(0); 409} 410 411/** 412 * xmlHashUpdateInternal: 413 * @hash: hash table 414 * @key: first string key 415 * @key2: second string key 416 * @key3: third string key 417 * @payload: pointer to the payload 418 * @dealloc: deallocator function for replaced item or NULL 419 * @update: whether existing entries should be updated 420 * 421 * Internal function to add or update hash entries. 422 */ 423ATTRIBUTE_NO_SANITIZE_INTEGER 424static int 425xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key, 426 const xmlChar *key2, const xmlChar *key3, 427 void *payload, xmlHashDeallocator dealloc, int update) { 428 xmlChar *copy, *copy2, *copy3; 429 xmlHashEntry *entry = NULL; 430 size_t lengths[3]; 431 unsigned hashValue; 432 int found = 0; 433 434 if ((hash == NULL) || (key == NULL)) 435 return(-1); 436 437 /* 438 * Check for an existing entry 439 */ 440 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths); 441 if (hash->size > 0) 442 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); 443 if (found) { 444 if (update) { 445 if (dealloc) 446 dealloc(entry->payload, entry->key); 447 entry->payload = payload; 448 return(0); 449 } else { 450 /* 451 * xmlHashAddEntry found an existing entry. 452 * 453 * TODO: We should return a different error code here to 454 * distinguish from malloc failures. 455 */ 456 return(-1); 457 } 458 } 459 460 /* 461 * Grow the hash table if needed 462 */ 463 if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) { 464 unsigned newSize, mask, displ, pos; 465 466 if (hash->size == 0) { 467 newSize = MIN_HASH_SIZE; 468 } else { 469 /* This guarantees that nbElems < INT_MAX */ 470 if (hash->size >= MAX_HASH_SIZE) 471 return(-1); 472 newSize = hash->size * 2; 473 } 474 if (xmlHashGrow(hash, newSize) != 0) 475 return(-1); 476 477 /* 478 * Find new entry 479 */ 480 mask = hash->size - 1; 481 displ = 0; 482 pos = hashValue & mask; 483 entry = &hash->table[pos]; 484 485 if (entry->hashValue != 0) { 486 do { 487 displ++; 488 pos++; 489 entry++; 490 if ((pos & mask) == 0) 491 entry = hash->table; 492 } while ((entry->hashValue != 0) && 493 ((pos - entry->hashValue) & mask) >= displ); 494 } 495 } 496 497 /* 498 * Copy keys 499 */ 500 if (hash->dict != NULL) { 501 if (xmlDictOwns(hash->dict, key)) { 502 copy = (xmlChar *) key; 503 } else { 504 copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1); 505 if (copy == NULL) 506 return(-1); 507 } 508 509 if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) { 510 copy2 = (xmlChar *) key2; 511 } else { 512 copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1); 513 if (copy2 == NULL) 514 return(-1); 515 } 516 if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) { 517 copy3 = (xmlChar *) key3; 518 } else { 519 copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1); 520 if (copy3 == NULL) 521 return(-1); 522 } 523 } else { 524 copy = xmlMalloc(lengths[0] + 1); 525 if (copy == NULL) 526 return(-1); 527 memcpy(copy, key, lengths[0] + 1); 528 529 if (key2 != NULL) { 530 copy2 = xmlMalloc(lengths[1] + 1); 531 if (copy2 == NULL) { 532 xmlFree(copy); 533 return(-1); 534 } 535 memcpy(copy2, key2, lengths[1] + 1); 536 } else { 537 copy2 = NULL; 538 } 539 540 if (key3 != NULL) { 541 copy3 = xmlMalloc(lengths[2] + 1); 542 if (copy3 == NULL) { 543 xmlFree(copy); 544 xmlFree(copy2); 545 return(-1); 546 } 547 memcpy(copy3, key3, lengths[2] + 1); 548 } else { 549 copy3 = NULL; 550 } 551 } 552 553 /* 554 * Shift the remainder of the probe sequence to the right 555 */ 556 if (entry->hashValue != 0) { 557 const xmlHashEntry *end = &hash->table[hash->size]; 558 const xmlHashEntry *cur = entry; 559 560 do { 561 cur++; 562 if (cur >= end) 563 cur = hash->table; 564 } while (cur->hashValue != 0); 565 566 if (cur < entry) { 567 /* 568 * If we traversed the end of the buffer, handle the part 569 * at the start of the buffer. 570 */ 571 memmove(&hash->table[1], hash->table, 572 (char *) cur - (char *) hash->table); 573 cur = end - 1; 574 hash->table[0] = *cur; 575 } 576 577 memmove(&entry[1], entry, (char *) cur - (char *) entry); 578 } 579 580 /* 581 * Populate entry 582 */ 583 entry->key = copy; 584 entry->key2 = copy2; 585 entry->key3 = copy3; 586 entry->payload = payload; 587 /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */ 588 entry->hashValue = hashValue | MAX_HASH_SIZE; 589 590 hash->nbElems++; 591 592 return(0); 593} 594 595/** 596 * xmlHashDefaultDeallocator: 597 * @entry: hash table entry 598 * @key: the entry's string key 599 * 600 * Free a hash table entry with xmlFree. 601 */ 602void 603xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) { 604 xmlFree(entry); 605} 606 607/** 608 * xmlHashAddEntry: 609 * @hash: hash table 610 * @key: string key 611 * @payload: pointer to the payload 612 * 613 * Add a hash table entry. If an entry with this key already exists, 614 * payload will not be updated and -1 is returned. This return value 615 * can't be distinguished from out-of-memory errors, so this function 616 * should be used with care. 617 * 618 * Returns 0 on success and -1 in case of error. 619 */ 620int 621xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) { 622 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0)); 623} 624 625/** 626 * xmlHashAddEntry2: 627 * @hash: hash table 628 * @key: first string key 629 * @key2: second string key 630 * @payload: pointer to the payload 631 * 632 * Add a hash table entry with two strings as key. 633 * 634 * See xmlHashAddEntry. 635 * 636 * Returns 0 on success and -1 in case of error. 637 */ 638int 639xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key, 640 const xmlChar *key2, void *payload) { 641 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0)); 642} 643 644/** 645 * xmlHashAddEntry3: 646 * @hash: hash table 647 * @key: first string key 648 * @key2: second string key 649 * @key3: third string key 650 * @payload: pointer to the payload 651 * 652 * Add a hash table entry with three strings as key. 653 * 654 * See xmlHashAddEntry. 655 * 656 * Returns 0 on success and -1 in case of error. 657 */ 658int 659xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key, 660 const xmlChar *key2, const xmlChar *key3, 661 void *payload) { 662 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0)); 663} 664 665/** 666 * xmlHashUpdateEntry: 667 * @hash: hash table 668 * @key: string key 669 * @payload: pointer to the payload 670 * @dealloc: deallocator function for replaced item or NULL 671 * 672 * Add a hash table entry. If an entry with this key already exists, 673 * the old payload will be freed and updated with the new value. 674 * 675 * Returns 0 in case of success, -1 if a memory allocation failed. 676 */ 677int 678xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key, 679 void *payload, xmlHashDeallocator dealloc) { 680 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, 681 dealloc, 1)); 682} 683 684/** 685 * xmlHashUpdateEntry2: 686 * @hash: hash table 687 * @key: first string key 688 * @key2: second string key 689 * @payload: pointer to the payload 690 * @dealloc: deallocator function for replaced item or NULL 691 * 692 * Add a hash table entry with two strings as key. 693 * 694 * See xmlHashUpdateEntry. 695 * 696 * Returns 0 on success and -1 in case of error. 697 */ 698int 699xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key, 700 const xmlChar *key2, void *payload, 701 xmlHashDeallocator dealloc) { 702 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, 703 dealloc, 1)); 704} 705 706/** 707 * xmlHashUpdateEntry3: 708 * @hash: hash table 709 * @key: first string key 710 * @key2: second string key 711 * @key3: third string key 712 * @payload: pointer to the payload 713 * @dealloc: deallocator function for replaced item or NULL 714 * 715 * Add a hash table entry with three strings as key. 716 * 717 * See xmlHashUpdateEntry. 718 * 719 * Returns 0 on success and -1 in case of error. 720 */ 721int 722xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key, 723 const xmlChar *key2, const xmlChar *key3, 724 void *payload, xmlHashDeallocator dealloc) { 725 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, 726 dealloc, 1)); 727} 728 729/** 730 * xmlHashLookup: 731 * @hash: hash table 732 * @key: string key 733 * 734 * Find the entry specified by @key. 735 * 736 * Returns a pointer to the payload or NULL if no entry was found. 737 */ 738void * 739xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) { 740 return(xmlHashLookup3(hash, key, NULL, NULL)); 741} 742 743/** 744 * xmlHashLookup2: 745 * @hash: hash table 746 * @key: first string key 747 * @key2: second string key 748 * 749 * Find the payload specified by the (@key, @key2) tuple. 750 * 751 * Returns a pointer to the payload or NULL if no entry was found. 752 */ 753void * 754xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key, 755 const xmlChar *key2) { 756 return(xmlHashLookup3(hash, key, key2, NULL)); 757} 758 759/** 760 * xmlHashQLookup: 761 * @hash: hash table 762 * @prefix: prefix of the string key 763 * @name: local name of the string key 764 * 765 * Find the payload specified by the QName @prefix:@name or @name. 766 * 767 * Returns a pointer to the payload or NULL if no entry was found. 768 */ 769void * 770xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix, 771 const xmlChar *name) { 772 return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL)); 773} 774 775/** 776 * xmlHashQLookup2: 777 * @hash: hash table 778 * @prefix: first prefix 779 * @name: first local name 780 * @prefix2: second prefix 781 * @name2: second local name 782 * 783 * Find the payload specified by the QNames tuple. 784 * 785 * Returns a pointer to the payload or NULL if no entry was found. 786 */ 787void * 788xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix, 789 const xmlChar *name, const xmlChar *prefix2, 790 const xmlChar *name2) { 791 return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL)); 792} 793 794/** 795 * xmlHashLookup3: 796 * @hash: hash table 797 * @key: first string key 798 * @key2: second string key 799 * @key3: third string key 800 * 801 * Find the payload specified by the (@key, @key2, @key3) tuple. 802 * 803 * Returns a pointer to the payload or NULL if no entry was found. 804 */ 805void * 806xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key, 807 const xmlChar *key2, const xmlChar *key3) { 808 const xmlHashEntry *entry; 809 unsigned hashValue; 810 int found; 811 812 if ((hash == NULL) || (hash->size == 0) || (key == NULL)) 813 return(NULL); 814 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL); 815 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); 816 if (found) 817 return(entry->payload); 818 return(NULL); 819} 820 821/** 822 * xmlHashQLookup3: 823 * @hash: hash table 824 * @prefix: first prefix 825 * @name: first local name 826 * @prefix2: second prefix 827 * @name2: second local name 828 * @prefix3: third prefix 829 * @name3: third local name 830 * 831 * Find the payload specified by the QNames tuple. 832 * 833 * Returns a pointer to the payload or NULL if no entry was found. 834 */ 835ATTRIBUTE_NO_SANITIZE_INTEGER 836void * 837xmlHashQLookup3(xmlHashTablePtr hash, 838 const xmlChar *prefix, const xmlChar *name, 839 const xmlChar *prefix2, const xmlChar *name2, 840 const xmlChar *prefix3, const xmlChar *name3) { 841 const xmlHashEntry *entry; 842 unsigned hashValue, mask, pos, displ; 843 844 if ((hash == NULL) || (hash->size == 0) || (name == NULL)) 845 return(NULL); 846 847 hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2, 848 name2, prefix3, name3); 849 mask = hash->size - 1; 850 pos = hashValue & mask; 851 entry = &hash->table[pos]; 852 853 if (entry->hashValue != 0) { 854 displ = 0; 855 hashValue |= MAX_HASH_SIZE; 856 857 do { 858 if ((hashValue == entry->hashValue) && 859 (xmlStrQEqual(prefix, name, entry->key)) && 860 (xmlStrQEqual(prefix2, name2, entry->key2)) && 861 (xmlStrQEqual(prefix3, name3, entry->key3))) 862 return(entry->payload); 863 864 displ++; 865 pos++; 866 entry++; 867 if ((pos & mask) == 0) 868 entry = hash->table; 869 } while ((entry->hashValue != 0) && 870 (((pos - entry->hashValue) & mask) >= displ)); 871 } 872 873 return(NULL); 874} 875 876typedef struct { 877 xmlHashScanner scan; 878 void *data; 879} stubData; 880 881static void 882stubHashScannerFull(void *payload, void *data, const xmlChar *key, 883 const xmlChar *key2 ATTRIBUTE_UNUSED, 884 const xmlChar *key3 ATTRIBUTE_UNUSED) { 885 stubData *sdata = (stubData *) data; 886 sdata->scan(payload, sdata->data, key); 887} 888 889/** 890 * xmlHashScan: 891 * @hash: hash table 892 * @scan: scanner function for items in the hash 893 * @data: extra data passed to @scan 894 * 895 * Scan the hash @table and apply @scan to each value. 896 */ 897void 898xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) { 899 stubData sdata; 900 sdata.data = data; 901 sdata.scan = scan; 902 xmlHashScanFull(hash, stubHashScannerFull, &sdata); 903} 904 905/** 906 * xmlHashScanFull: 907 * @hash: hash table 908 * @scan: scanner function for items in the hash 909 * @data: extra data passed to @scan 910 * 911 * Scan the hash @table and apply @scan to each value. 912 */ 913void 914xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) { 915 const xmlHashEntry *entry, *end; 916 xmlHashEntry old; 917 unsigned i; 918 919 if ((hash == NULL) || (hash->size == 0) || (scan == NULL)) 920 return; 921 922 /* 923 * We must handle the case that a scanned entry is removed when executing 924 * the callback (xmlCleanSpecialAttr and possibly other places). 925 * 926 * Find the start of a probe sequence to avoid scanning entries twice if 927 * a deletion happens. 928 */ 929 entry = hash->table; 930 end = &hash->table[hash->size]; 931 while (entry->hashValue != 0) { 932 if (++entry >= end) 933 entry = hash->table; 934 } 935 936 for (i = 0; i < hash->size; i++) { 937 if ((entry->hashValue != 0) && (entry->payload != NULL)) { 938 /* 939 * Make sure to rescan after a possible deletion. 940 */ 941 do { 942 old = *entry; 943 scan(entry->payload, data, entry->key, entry->key2, entry->key3); 944 } while ((entry->hashValue != 0) && 945 (entry->payload != NULL) && 946 ((entry->key != old.key) || 947 (entry->key2 != old.key2) || 948 (entry->key3 != old.key3))); 949 } 950 if (++entry >= end) 951 entry = hash->table; 952 } 953} 954 955/** 956 * xmlHashScan3: 957 * @hash: hash table 958 * @key: first string key or NULL 959 * @key2: second string key or NULL 960 * @key3: third string key or NULL 961 * @scan: scanner function for items in the hash 962 * @data: extra data passed to @scan 963 * 964 * Scan the hash @table and apply @scan to each value matching 965 * (@key, @key2, @key3) tuple. If one of the keys is null, 966 * the comparison is considered to match. 967 */ 968void 969xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key, 970 const xmlChar *key2, const xmlChar *key3, 971 xmlHashScanner scan, void *data) { 972 stubData sdata; 973 sdata.data = data; 974 sdata.scan = scan; 975 xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata); 976} 977 978/** 979 * xmlHashScanFull3: 980 * @hash: hash table 981 * @key: first string key or NULL 982 * @key2: second string key or NULL 983 * @key3: third string key or NULL 984 * @scan: scanner function for items in the hash 985 * @data: extra data passed to @scan 986 * 987 * Scan the hash @table and apply @scan to each value matching 988 * (@key, @key2, @key3) tuple. If one of the keys is null, 989 * the comparison is considered to match. 990 */ 991void 992xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key, 993 const xmlChar *key2, const xmlChar *key3, 994 xmlHashScannerFull scan, void *data) { 995 const xmlHashEntry *entry, *end; 996 xmlHashEntry old; 997 unsigned i; 998 999 if ((hash == NULL) || (hash->size == 0) || (scan == NULL)) 1000 return; 1001 1002 /* 1003 * We must handle the case that a scanned entry is removed when executing 1004 * the callback (xmlCleanSpecialAttr and possibly other places). 1005 * 1006 * Find the start of a probe sequence to avoid scanning entries twice if 1007 * a deletion happens. 1008 */ 1009 entry = hash->table; 1010 end = &hash->table[hash->size]; 1011 while (entry->hashValue != 0) { 1012 if (++entry >= end) 1013 entry = hash->table; 1014 } 1015 1016 for (i = 0; i < hash->size; i++) { 1017 if ((entry->hashValue != 0) && (entry->payload != NULL)) { 1018 /* 1019 * Make sure to rescan after a possible deletion. 1020 */ 1021 do { 1022 if (((key != NULL) && (strcmp((const char *) key, 1023 (const char *) entry->key) != 0)) || 1024 ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) || 1025 ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3)))) 1026 break; 1027 old = *entry; 1028 scan(entry->payload, data, entry->key, entry->key2, entry->key3); 1029 } while ((entry->hashValue != 0) && 1030 (entry->payload != NULL) && 1031 ((entry->key != old.key) || 1032 (entry->key2 != old.key2) || 1033 (entry->key3 != old.key3))); 1034 } 1035 if (++entry >= end) 1036 entry = hash->table; 1037 } 1038} 1039 1040/** 1041 * xmlHashCopy: 1042 * @hash: hash table 1043 * @copy: copier function for items in the hash 1044 * 1045 * Copy the hash @table using @copy to copy payloads. 1046 * 1047 * Returns the new table or NULL if a memory allocation failed. 1048 */ 1049xmlHashTablePtr 1050xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) { 1051 const xmlHashEntry *entry, *end; 1052 xmlHashTablePtr ret; 1053 1054 if ((hash == NULL) || (copy == NULL)) 1055 return(NULL); 1056 1057 ret = xmlHashCreate(hash->size); 1058 if (ret == NULL) 1059 return(NULL); 1060 1061 if (hash->size == 0) 1062 return(ret); 1063 1064 end = &hash->table[hash->size]; 1065 1066 for (entry = hash->table; entry < end; entry++) { 1067 if (entry->hashValue != 0) 1068 xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3, 1069 copy(entry->payload, entry->key)); 1070 } 1071 1072 return(ret); 1073} 1074 1075/** 1076 * xmlHashSize: 1077 * @hash: hash table 1078 * 1079 * Query the number of elements in the hash table. 1080 * 1081 * Returns the number of elements in the hash table or 1082 * -1 in case of error. 1083 */ 1084int 1085xmlHashSize(xmlHashTablePtr hash) { 1086 if (hash == NULL) 1087 return(-1); 1088 return(hash->nbElems); 1089} 1090 1091/** 1092 * xmlHashRemoveEntry: 1093 * @hash: hash table 1094 * @key: string key 1095 * @dealloc: deallocator function for removed item or NULL 1096 * 1097 * Find the entry specified by the @key and remove it from the hash table. 1098 * Payload will be freed with @dealloc. 1099 * 1100 * Returns 0 on success and -1 if no entry was found. 1101 */ 1102int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key, 1103 xmlHashDeallocator dealloc) { 1104 return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc)); 1105} 1106 1107/** 1108 * xmlHashRemoveEntry2: 1109 * @hash: hash table 1110 * @key: first string key 1111 * @key2: second string key 1112 * @dealloc: deallocator function for removed item or NULL 1113 * 1114 * Remove an entry with two strings as key. 1115 * 1116 * See xmlHashRemoveEntry. 1117 * 1118 * Returns 0 on success and -1 in case of error. 1119 */ 1120int 1121xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key, 1122 const xmlChar *key2, xmlHashDeallocator dealloc) { 1123 return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc)); 1124} 1125 1126/** 1127 * xmlHashRemoveEntry3: 1128 * @hash: hash table 1129 * @key: first string key 1130 * @key2: second string key 1131 * @key3: third string key 1132 * @dealloc: deallocator function for removed item or NULL 1133 * 1134 * Remove an entry with three strings as key. 1135 * 1136 * See xmlHashRemoveEntry. 1137 * 1138 * Returns 0 on success and -1 in case of error. 1139 */ 1140ATTRIBUTE_NO_SANITIZE_INTEGER 1141int 1142xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key, 1143 const xmlChar *key2, const xmlChar *key3, 1144 xmlHashDeallocator dealloc) { 1145 xmlHashEntry *entry, *cur, *next; 1146 unsigned hashValue, mask, pos, nextpos; 1147 int found; 1148 1149 if ((hash == NULL) || (hash->size == 0) || (key == NULL)) 1150 return(-1); 1151 1152 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL); 1153 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); 1154 if (!found) 1155 return(-1); 1156 1157 if ((dealloc != NULL) && (entry->payload != NULL)) 1158 dealloc(entry->payload, entry->key); 1159 if (hash->dict == NULL) { 1160 if (entry->key) 1161 xmlFree(entry->key); 1162 if (entry->key2) 1163 xmlFree(entry->key2); 1164 if (entry->key3) 1165 xmlFree(entry->key3); 1166 } 1167 1168 /* 1169 * Find end of probe sequence. Entries at their initial probe 1170 * position start a new sequence. 1171 */ 1172 mask = hash->size - 1; 1173 pos = entry - hash->table; 1174 cur = entry; 1175 1176 while (1) { 1177 nextpos = pos + 1; 1178 next = cur + 1; 1179 if ((nextpos & mask) == 0) 1180 next = hash->table; 1181 1182 if ((next->hashValue == 0) || 1183 (((next->hashValue - nextpos) & mask) == 0)) 1184 break; 1185 1186 cur = next; 1187 pos = nextpos; 1188 } 1189 1190 /* 1191 * Backward shift 1192 */ 1193 next = entry + 1; 1194 1195 if (cur < entry) { 1196 xmlHashEntry *end = &hash->table[hash->size]; 1197 1198 memmove(entry, next, (char *) end - (char *) next); 1199 entry = hash->table; 1200 end[-1] = *entry; 1201 next = entry + 1; 1202 } 1203 1204 memmove(entry, next, (char *) cur - (char *) entry); 1205 1206 /* 1207 * Update entry 1208 */ 1209 cur->hashValue = 0; 1210 1211 hash->nbElems--; 1212 1213 return(0); 1214}