1 /* 2 * dict.c: dictionary of reusable strings, just used to avoid allocation 3 * and freeing operations. 4 * 5 * Copyright (C) 2003-2012 Daniel Veillard. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 12 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 13 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND 14 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. 15 * 16 * Author: daniel@veillard.com 17 */ 18 19 #define IN_LIBXML 20 #include "libxml.h" 21 22 #include <limits.h> 23 #ifdef HAVE_STDLIB_H 24 #include <stdlib.h> 25 #endif 26 #ifdef HAVE_TIME_H 27 #include <time.h> 28 #endif 29 30 /* 31 * Following http://www.ocert.org/advisories/ocert-2011-003.html 32 * it seems that having hash randomization might be a good idea 33 * when using XML with untrusted data 34 * Note1: that it works correctly only if compiled with WITH_BIG_KEY 35 * which is the default. 36 * Note2: the fast function used for a small dict won't protect very 37 * well but since the attack is based on growing a very big hash 38 * list we will use the BigKey algo as soon as the hash size grows 39 * over MIN_DICT_SIZE so this actually works 40 */ 41 #if defined(HAVE_RAND) && defined(HAVE_SRAND) && defined(HAVE_TIME) && \ 42 !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) 43 #define DICT_RANDOMIZATION 44 #endif 45 46 #include <string.h> 47 #ifdef HAVE_STDINT_H 48 #include <stdint.h> 49 #else 50 #ifdef HAVE_INTTYPES_H 51 #include <inttypes.h> 52 #elif defined(_WIN32) 53 typedef unsigned __int32 uint32_t; 54 #endif 55 #endif 56 #include <libxml/tree.h> 57 #include <libxml/dict.h> 58 #include <libxml/xmlmemory.h> 59 #include <libxml/xmlerror.h> 60 #include <libxml/globals.h> 61 62 /* #define DEBUG_GROW */ 63 /* #define DICT_DEBUG_PATTERNS */ 64 65 #define MAX_HASH_LEN 3 66 #define MIN_DICT_SIZE 128 67 #define MAX_DICT_HASH 8 * 2048 68 #define WITH_BIG_KEY 69 70 #ifdef WITH_BIG_KEY 71 #define xmlDictComputeKey(dict, name, len) \ 72 (((dict)->size == MIN_DICT_SIZE) ? \ 73 xmlDictComputeFastKey(name, len, (dict)->seed) : \ 74 xmlDictComputeBigKey(name, len, (dict)->seed)) 75 76 #define xmlDictComputeQKey(dict, prefix, plen, name, len) \ 77 (((prefix) == NULL) ? \ 78 (xmlDictComputeKey(dict, name, len)) : \ 79 (((dict)->size == MIN_DICT_SIZE) ? \ 80 xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) : \ 81 xmlDictComputeBigQKey(prefix, plen, name, len, (dict)->seed))) 82 83 #else /* !WITH_BIG_KEY */ 84 #define xmlDictComputeKey(dict, name, len) \ 85 xmlDictComputeFastKey(name, len, (dict)->seed) 86 #define xmlDictComputeQKey(dict, prefix, plen, name, len) \ 87 xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) 88 #endif /* WITH_BIG_KEY */ 89 90 /* 91 * An entry in the dictionary 92 */ 93 typedef struct _xmlDictEntry xmlDictEntry; 94 typedef xmlDictEntry *xmlDictEntryPtr; 95 struct _xmlDictEntry { 96 struct _xmlDictEntry *next; 97 const xmlChar *name; 98 unsigned int len; 99 int valid; 100 unsigned long okey; 101 }; 102 103 typedef struct _xmlDictStrings xmlDictStrings; 104 typedef xmlDictStrings *xmlDictStringsPtr; 105 struct _xmlDictStrings { 106 xmlDictStringsPtr next; 107 xmlChar *free; 108 xmlChar *end; 109 size_t size; 110 size_t nbStrings; 111 xmlChar array[1]; 112 }; 113 /* 114 * The entire dictionary 115 */ 116 struct _xmlDict { 117 int ref_counter; 118 119 struct _xmlDictEntry *dict; 120 size_t size; 121 unsigned int nbElems; 122 xmlDictStringsPtr strings; 123 124 struct _xmlDict *subdict; 125 /* used for randomization */ 126 int seed; 127 /* used to impose a limit on size */ 128 size_t limit; 129 }; 130 131 /* 132 * A mutex for modifying the reference counter for shared 133 * dictionaries. 134 */ 135 static xmlRMutexPtr xmlDictMutex = NULL; 136 137 /* 138 * Whether the dictionary mutex was initialized. 139 */ 140 static int xmlDictInitialized = 0; 141 142 #ifdef DICT_RANDOMIZATION 143 #ifdef HAVE_RAND_R 144 /* 145 * Internal data for random function, protected by xmlDictMutex 146 */ 147 static unsigned int rand_seed = 0; 148 #endif 149 #endif 150 151 /** 152 * xmlInitializeDict: 153 * 154 * Do the dictionary mutex initialization. 155 * this function is deprecated 156 * 157 * Returns 0 if initialization was already done, and 1 if that 158 * call led to the initialization 159 */ 160 int xmlInitializeDict(void) { 161 return(0); 162 } 163 164 /** 165 * __xmlInitializeDict: 166 * 167 * This function is not public 168 * Do the dictionary mutex initialization. 169 * this function is not thread safe, initialization should 170 * normally be done once at setup when called from xmlOnceInit() 171 * we may also land in this code if thread support is not compiled in 172 * 173 * Returns 0 if initialization was already done, and 1 if that 174 * call led to the initialization 175 */ 176 int __xmlInitializeDict(void) { 177 if (xmlDictInitialized) 178 return(1); 179 180 if ((xmlDictMutex = xmlNewRMutex()) == NULL) 181 return(0); 182 xmlRMutexLock(xmlDictMutex); 183 184 #ifdef DICT_RANDOMIZATION 185 #ifdef HAVE_RAND_R 186 rand_seed = time(NULL); 187 rand_r(& rand_seed); 188 #else 189 srand(time(NULL)); 190 #endif 191 #endif 192 xmlDictInitialized = 1; 193 xmlRMutexUnlock(xmlDictMutex); 194 return(1); 195 } 196 197 #ifdef DICT_RANDOMIZATION 198 int __xmlRandom(void) { 199 int ret; 200 201 if (xmlDictInitialized == 0) 202 __xmlInitializeDict(); 203 204 xmlRMutexLock(xmlDictMutex); 205 #ifdef HAVE_RAND_R 206 ret = rand_r(& rand_seed); 207 #else 208 ret = rand(); 209 #endif 210 xmlRMutexUnlock(xmlDictMutex); 211 return(ret); 212 } 213 #endif 214 215 /** 216 * xmlDictCleanup: 217 * 218 * Free the dictionary mutex. Do not call unless sure the library 219 * is not in use anymore ! 220 */ 221 void 222 xmlDictCleanup(void) { 223 if (!xmlDictInitialized) 224 return; 225 226 xmlFreeRMutex(xmlDictMutex); 227 228 xmlDictInitialized = 0; 229 } 230 231 /* 232 * xmlDictAddString: 233 * @dict: the dictionary 234 * @name: the name of the userdata 235 * @len: the length of the name 236 * 237 * Add the string to the array[s] 238 * 239 * Returns the pointer of the local string, or NULL in case of error. 240 */ 241 static const xmlChar * 242 xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) { 243 xmlDictStringsPtr pool; 244 const xmlChar *ret; 245 size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ 246 size_t limit = 0; 247 248 #ifdef DICT_DEBUG_PATTERNS 249 fprintf(stderr, "-"); 250 #endif 251 pool = dict->strings; 252 while (pool != NULL) { 253 if ((size_t)(pool->end - pool->free) > namelen) 254 goto found_pool; 255 if (pool->size > size) size = pool->size; 256 limit += pool->size; 257 pool = pool->next; 258 } 259 /* 260 * Not found, need to allocate 261 */ 262 if (pool == NULL) { 263 if ((dict->limit > 0) && (limit > dict->limit)) { 264 return(NULL); 265 } 266 267 if (size == 0) size = 1000; 268 else size *= 4; /* exponential growth */ 269 if (size < 4 * namelen) 270 size = 4 * namelen; /* just in case ! */ 271 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); 272 if (pool == NULL) 273 return(NULL); 274 pool->size = size; 275 pool->nbStrings = 0; 276 pool->free = &pool->array[0]; 277 pool->end = &pool->array[size]; 278 pool->next = dict->strings; 279 dict->strings = pool; 280 #ifdef DICT_DEBUG_PATTERNS 281 fprintf(stderr, "+"); 282 #endif 283 } 284 found_pool: 285 ret = pool->free; 286 memcpy(pool->free, name, namelen); 287 pool->free += namelen; 288 *(pool->free++) = 0; 289 pool->nbStrings++; 290 return(ret); 291 } 292 293 /* 294 * xmlDictAddQString: 295 * @dict: the dictionary 296 * @prefix: the prefix of the userdata 297 * @plen: the prefix length 298 * @name: the name of the userdata 299 * @len: the length of the name 300 * 301 * Add the QName to the array[s] 302 * 303 * Returns the pointer of the local string, or NULL in case of error. 304 */ 305 static const xmlChar * 306 xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen, 307 const xmlChar *name, unsigned int namelen) 308 { 309 xmlDictStringsPtr pool; 310 const xmlChar *ret; 311 size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ 312 size_t limit = 0; 313 314 if (prefix == NULL) return(xmlDictAddString(dict, name, namelen)); 315 316 #ifdef DICT_DEBUG_PATTERNS 317 fprintf(stderr, "="); 318 #endif 319 pool = dict->strings; 320 while (pool != NULL) { 321 if ((size_t)(pool->end - pool->free) > namelen + plen + 1) 322 goto found_pool; 323 if (pool->size > size) size = pool->size; 324 limit += pool->size; 325 pool = pool->next; 326 } 327 /* 328 * Not found, need to allocate 329 */ 330 if (pool == NULL) { 331 if ((dict->limit > 0) && (limit > dict->limit)) { 332 return(NULL); 333 } 334 335 if (size == 0) size = 1000; 336 else size *= 4; /* exponential growth */ 337 if (size < 4 * (namelen + plen + 1)) 338 size = 4 * (namelen + plen + 1); /* just in case ! */ 339 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); 340 if (pool == NULL) 341 return(NULL); 342 pool->size = size; 343 pool->nbStrings = 0; 344 pool->free = &pool->array[0]; 345 pool->end = &pool->array[size]; 346 pool->next = dict->strings; 347 dict->strings = pool; 348 #ifdef DICT_DEBUG_PATTERNS 349 fprintf(stderr, "+"); 350 #endif 351 } 352 found_pool: 353 ret = pool->free; 354 memcpy(pool->free, prefix, plen); 355 pool->free += plen; 356 *(pool->free++) = ':'; 357 memcpy(pool->free, name, namelen); 358 pool->free += namelen; 359 *(pool->free++) = 0; 360 pool->nbStrings++; 361 return(ret); 362 } 363 364 #ifdef WITH_BIG_KEY 365 /* 366 * xmlDictComputeBigKey: 367 * 368 * Calculate a hash key using a good hash function that works well for 369 * larger hash table sizes. 370 * 371 * Hash function by "One-at-a-Time Hash" see 372 * http://burtleburtle.net/bob/hash/doobs.html 373 */ 374 375 #ifdef __clang__ 376 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") 377 #endif 378 static uint32_t 379 xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) { 380 uint32_t hash; 381 int i; 382 383 if (namelen <= 0 || data == NULL) return(0); 384 385 hash = seed; 386 387 for (i = 0;i < namelen; i++) { 388 hash += data[i]; 389 hash += (hash << 10); 390 hash ^= (hash >> 6); 391 } 392 hash += (hash << 3); 393 hash ^= (hash >> 11); 394 hash += (hash << 15); 395 396 return hash; 397 } 398 399 /* 400 * xmlDictComputeBigQKey: 401 * 402 * Calculate a hash key for two strings using a good hash function 403 * that works well for larger hash table sizes. 404 * 405 * Hash function by "One-at-a-Time Hash" see 406 * http://burtleburtle.net/bob/hash/doobs.html 407 * 408 * Neither of the two strings must be NULL. 409 */ 410 #ifdef __clang__ 411 ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") 412 #endif 413 static unsigned long 414 xmlDictComputeBigQKey(const xmlChar *prefix, int plen, 415 const xmlChar *name, int len, int seed) 416 { 417 uint32_t hash; 418 int i; 419 420 hash = seed; 421 422 for (i = 0;i < plen; i++) { 423 hash += prefix[i]; 424 hash += (hash << 10); 425 hash ^= (hash >> 6); 426 } 427 hash += ':'; 428 hash += (hash << 10); 429 hash ^= (hash >> 6); 430 431 for (i = 0;i < len; i++) { 432 hash += name[i]; 433 hash += (hash << 10); 434 hash ^= (hash >> 6); 435 } 436 hash += (hash << 3); 437 hash ^= (hash >> 11); 438 hash += (hash << 15); 439 440 return hash; 441 } 442 #endif /* WITH_BIG_KEY */ 443 444 /* 445 * xmlDictComputeFastKey: 446 * 447 * Calculate a hash key using a fast hash function that works well 448 * for low hash table fill. 449 */ 450 static unsigned long 451 xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) { 452 unsigned long value = seed; 453 454 if (name == NULL) return(0); 455 value = *name; 456 value <<= 5; 457 if (namelen > 10) { 458 value += name[namelen - 1]; 459 namelen = 10; 460 } 461 switch (namelen) { 462 case 10: value += name[9]; 463 /* Falls through. */ 464 case 9: value += name[8]; 465 /* Falls through. */ 466 case 8: value += name[7]; 467 /* Falls through. */ 468 case 7: value += name[6]; 469 /* Falls through. */ 470 case 6: value += name[5]; 471 /* Falls through. */ 472 case 5: value += name[4]; 473 /* Falls through. */ 474 case 4: value += name[3]; 475 /* Falls through. */ 476 case 3: value += name[2]; 477 /* Falls through. */ 478 case 2: value += name[1]; 479 /* Falls through. */ 480 default: break; 481 } 482 return(value); 483 } 484 485 /* 486 * xmlDictComputeFastQKey: 487 * 488 * Calculate a hash key for two strings using a fast hash function 489 * that works well for low hash table fill. 490 * 491 * Neither of the two strings must be NULL. 492 */ 493 static unsigned long 494 xmlDictComputeFastQKey(const xmlChar *prefix, int plen, 495 const xmlChar *name, int len, int seed) 496 { 497 unsigned long value = (unsigned long) seed; 498 499 if (plen == 0) 500 value += 30 * (unsigned long) ':'; 501 else 502 value += 30 * (*prefix); 503 504 if (len > 10) { 505 int offset = len - (plen + 1 + 1); 506 if (offset < 0) 507 offset = len - (10 + 1); 508 value += name[offset]; 509 len = 10; 510 if (plen > 10) 511 plen = 10; 512 } 513 switch (plen) { 514 case 10: value += prefix[9]; 515 /* Falls through. */ 516 case 9: value += prefix[8]; 517 /* Falls through. */ 518 case 8: value += prefix[7]; 519 /* Falls through. */ 520 case 7: value += prefix[6]; 521 /* Falls through. */ 522 case 6: value += prefix[5]; 523 /* Falls through. */ 524 case 5: value += prefix[4]; 525 /* Falls through. */ 526 case 4: value += prefix[3]; 527 /* Falls through. */ 528 case 3: value += prefix[2]; 529 /* Falls through. */ 530 case 2: value += prefix[1]; 531 /* Falls through. */ 532 case 1: value += prefix[0]; 533 /* Falls through. */ 534 default: break; 535 } 536 len -= plen; 537 if (len > 0) { 538 value += (unsigned long) ':'; 539 len--; 540 } 541 switch (len) { 542 case 10: value += name[9]; 543 /* Falls through. */ 544 case 9: value += name[8]; 545 /* Falls through. */ 546 case 8: value += name[7]; 547 /* Falls through. */ 548 case 7: value += name[6]; 549 /* Falls through. */ 550 case 6: value += name[5]; 551 /* Falls through. */ 552 case 5: value += name[4]; 553 /* Falls through. */ 554 case 4: value += name[3]; 555 /* Falls through. */ 556 case 3: value += name[2]; 557 /* Falls through. */ 558 case 2: value += name[1]; 559 /* Falls through. */ 560 case 1: value += name[0]; 561 /* Falls through. */ 562 default: break; 563 } 564 return(value); 565 } 566 567 /** 568 * xmlDictCreate: 569 * 570 * Create a new dictionary 571 * 572 * Returns the newly created dictionary, or NULL if an error occurred. 573 */ 574 xmlDictPtr 575 xmlDictCreate(void) { 576 xmlDictPtr dict; 577 578 if (!xmlDictInitialized) 579 if (!__xmlInitializeDict()) 580 return(NULL); 581 582 #ifdef DICT_DEBUG_PATTERNS 583 fprintf(stderr, "C"); 584 #endif 585 586 dict = xmlMalloc(sizeof(xmlDict)); 587 if (dict) { 588 dict->ref_counter = 1; 589 dict->limit = 0; 590 591 dict->size = MIN_DICT_SIZE; 592 dict->nbElems = 0; 593 dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry)); 594 dict->strings = NULL; 595 dict->subdict = NULL; 596 if (dict->dict) { 597 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry)); 598 #ifdef DICT_RANDOMIZATION 599 dict->seed = __xmlRandom(); 600 #else 601 dict->seed = 0; 602 #endif 603 return(dict); 604 } 605 xmlFree(dict); 606 } 607 return(NULL); 608 } 609 610 /** 611 * xmlDictCreateSub: 612 * @sub: an existing dictionary 613 * 614 * Create a new dictionary, inheriting strings from the read-only 615 * dictionary @sub. On lookup, strings are first searched in the 616 * new dictionary, then in @sub, and if not found are created in the 617 * new dictionary. 618 * 619 * Returns the newly created dictionary, or NULL if an error occurred. 620 */ 621 xmlDictPtr 622 xmlDictCreateSub(xmlDictPtr sub) { 623 xmlDictPtr dict = xmlDictCreate(); 624 625 if ((dict != NULL) && (sub != NULL)) { 626 #ifdef DICT_DEBUG_PATTERNS 627 fprintf(stderr, "R"); 628 #endif 629 dict->seed = sub->seed; 630 dict->subdict = sub; 631 xmlDictReference(dict->subdict); 632 } 633 return(dict); 634 } 635 636 /** 637 * xmlDictReference: 638 * @dict: the dictionary 639 * 640 * Increment the reference counter of a dictionary 641 * 642 * Returns 0 in case of success and -1 in case of error 643 */ 644 int 645 xmlDictReference(xmlDictPtr dict) { 646 if (!xmlDictInitialized) 647 if (!__xmlInitializeDict()) 648 return(-1); 649 650 if (dict == NULL) return -1; 651 xmlRMutexLock(xmlDictMutex); 652 dict->ref_counter++; 653 xmlRMutexUnlock(xmlDictMutex); 654 return(0); 655 } 656 657 /** 658 * xmlDictGrow: 659 * @dict: the dictionary 660 * @size: the new size of the dictionary 661 * 662 * resize the dictionary 663 * 664 * Returns 0 in case of success, -1 in case of failure 665 */ 666 static int 667 xmlDictGrow(xmlDictPtr dict, size_t size) { 668 unsigned long key, okey; 669 size_t oldsize, i; 670 xmlDictEntryPtr iter, next; 671 struct _xmlDictEntry *olddict; 672 #ifdef DEBUG_GROW 673 unsigned long nbElem = 0; 674 #endif 675 int ret = 0; 676 int keep_keys = 1; 677 678 if (dict == NULL) 679 return(-1); 680 if (size < 8) 681 return(-1); 682 if (size > 8 * 2048) 683 return(-1); 684 685 #ifdef DICT_DEBUG_PATTERNS 686 fprintf(stderr, "*"); 687 #endif 688 689 oldsize = dict->size; 690 olddict = dict->dict; 691 if (olddict == NULL) 692 return(-1); 693 if (oldsize == MIN_DICT_SIZE) 694 keep_keys = 0; 695 696 dict->dict = xmlMalloc(size * sizeof(xmlDictEntry)); 697 if (dict->dict == NULL) { 698 dict->dict = olddict; 699 return(-1); 700 } 701 memset(dict->dict, 0, size * sizeof(xmlDictEntry)); 702 dict->size = size; 703 704 /* If the two loops are merged, there would be situations where 705 a new entry needs to allocated and data copied into it from 706 the main dict. It is nicer to run through the array twice, first 707 copying all the elements in the main array (less probability of 708 allocate) and then the rest, so we only free in the second loop. 709 */ 710 for (i = 0; i < oldsize; i++) { 711 if (olddict[i].valid == 0) 712 continue; 713 714 if (keep_keys) 715 okey = olddict[i].okey; 716 else 717 okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len); 718 key = okey % dict->size; 719 720 if (dict->dict[key].valid == 0) { 721 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry)); 722 dict->dict[key].next = NULL; 723 dict->dict[key].okey = okey; 724 } else { 725 xmlDictEntryPtr entry; 726 727 entry = xmlMalloc(sizeof(xmlDictEntry)); 728 if (entry != NULL) { 729 entry->name = olddict[i].name; 730 entry->len = olddict[i].len; 731 entry->okey = okey; 732 entry->next = dict->dict[key].next; 733 entry->valid = 1; 734 dict->dict[key].next = entry; 735 } else { 736 /* 737 * we don't have much ways to alert from here 738 * result is losing an entry and unicity guarantee 739 */ 740 ret = -1; 741 } 742 } 743 #ifdef DEBUG_GROW 744 nbElem++; 745 #endif 746 } 747 748 for (i = 0; i < oldsize; i++) { 749 iter = olddict[i].next; 750 while (iter) { 751 next = iter->next; 752 753 /* 754 * put back the entry in the new dict 755 */ 756 757 if (keep_keys) 758 okey = iter->okey; 759 else 760 okey = xmlDictComputeKey(dict, iter->name, iter->len); 761 key = okey % dict->size; 762 if (dict->dict[key].valid == 0) { 763 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry)); 764 dict->dict[key].next = NULL; 765 dict->dict[key].valid = 1; 766 dict->dict[key].okey = okey; 767 xmlFree(iter); 768 } else { 769 iter->next = dict->dict[key].next; 770 iter->okey = okey; 771 dict->dict[key].next = iter; 772 } 773 774 #ifdef DEBUG_GROW 775 nbElem++; 776 #endif 777 778 iter = next; 779 } 780 } 781 782 xmlFree(olddict); 783 784 #ifdef DEBUG_GROW 785 xmlGenericError(xmlGenericErrorContext, 786 "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem); 787 #endif 788 789 return(ret); 790 } 791 792 /** 793 * xmlDictFree: 794 * @dict: the dictionary 795 * 796 * Free the hash @dict and its contents. The userdata is 797 * deallocated with @f if provided. 798 */ 799 void 800 xmlDictFree(xmlDictPtr dict) { 801 size_t i; 802 xmlDictEntryPtr iter; 803 xmlDictEntryPtr next; 804 int inside_dict = 0; 805 xmlDictStringsPtr pool, nextp; 806 807 if (dict == NULL) 808 return; 809 810 if (!xmlDictInitialized) 811 if (!__xmlInitializeDict()) 812 return; 813 814 /* decrement the counter, it may be shared by a parser and docs */ 815 xmlRMutexLock(xmlDictMutex); 816 dict->ref_counter--; 817 if (dict->ref_counter > 0) { 818 xmlRMutexUnlock(xmlDictMutex); 819 return; 820 } 821 822 xmlRMutexUnlock(xmlDictMutex); 823 824 if (dict->subdict != NULL) { 825 xmlDictFree(dict->subdict); 826 } 827 828 if (dict->dict) { 829 for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) { 830 iter = &(dict->dict[i]); 831 if (iter->valid == 0) 832 continue; 833 inside_dict = 1; 834 while (iter) { 835 next = iter->next; 836 if (!inside_dict) 837 xmlFree(iter); 838 dict->nbElems--; 839 inside_dict = 0; 840 iter = next; 841 } 842 } 843 xmlFree(dict->dict); 844 } 845 pool = dict->strings; 846 while (pool != NULL) { 847 nextp = pool->next; 848 xmlFree(pool); 849 pool = nextp; 850 } 851 xmlFree(dict); 852 } 853 854 /** 855 * xmlDictLookup: 856 * @dict: the dictionary 857 * @name: the name of the userdata 858 * @len: the length of the name, if -1 it is recomputed 859 * 860 * Add the @name to the dictionary @dict if not present. 861 * 862 * Returns the internal copy of the name or NULL in case of internal error 863 */ 864 const xmlChar * 865 xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) { 866 unsigned long key, okey, nbi = 0; 867 xmlDictEntryPtr entry; 868 xmlDictEntryPtr insert; 869 const xmlChar *ret; 870 unsigned int l; 871 872 if ((dict == NULL) || (name == NULL)) 873 return(NULL); 874 875 if (len < 0) 876 l = strlen((const char *) name); 877 else 878 l = len; 879 880 if (((dict->limit > 0) && (l >= dict->limit)) || 881 (l > INT_MAX / 2)) 882 return(NULL); 883 884 /* 885 * Check for duplicate and insertion location. 886 */ 887 okey = xmlDictComputeKey(dict, name, l); 888 key = okey % dict->size; 889 if (dict->dict[key].valid == 0) { 890 insert = NULL; 891 } else { 892 for (insert = &(dict->dict[key]); insert->next != NULL; 893 insert = insert->next) { 894 #ifdef __GNUC__ 895 if ((insert->okey == okey) && (insert->len == l)) { 896 if (!memcmp(insert->name, name, l)) 897 return(insert->name); 898 } 899 #else 900 if ((insert->okey == okey) && (insert->len == l) && 901 (!xmlStrncmp(insert->name, name, l))) 902 return(insert->name); 903 #endif 904 nbi++; 905 } 906 #ifdef __GNUC__ 907 if ((insert->okey == okey) && (insert->len == l)) { 908 if (!memcmp(insert->name, name, l)) 909 return(insert->name); 910 } 911 #else 912 if ((insert->okey == okey) && (insert->len == l) && 913 (!xmlStrncmp(insert->name, name, l))) 914 return(insert->name); 915 #endif 916 } 917 918 if (dict->subdict) { 919 unsigned long skey; 920 921 /* we cannot always reuse the same okey for the subdict */ 922 if (((dict->size == MIN_DICT_SIZE) && 923 (dict->subdict->size != MIN_DICT_SIZE)) || 924 ((dict->size != MIN_DICT_SIZE) && 925 (dict->subdict->size == MIN_DICT_SIZE))) 926 skey = xmlDictComputeKey(dict->subdict, name, l); 927 else 928 skey = okey; 929 930 key = skey % dict->subdict->size; 931 if (dict->subdict->dict[key].valid != 0) { 932 xmlDictEntryPtr tmp; 933 934 for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL; 935 tmp = tmp->next) { 936 #ifdef __GNUC__ 937 if ((tmp->okey == skey) && (tmp->len == l)) { 938 if (!memcmp(tmp->name, name, l)) 939 return(tmp->name); 940 } 941 #else 942 if ((tmp->okey == skey) && (tmp->len == l) && 943 (!xmlStrncmp(tmp->name, name, l))) 944 return(tmp->name); 945 #endif 946 nbi++; 947 } 948 #ifdef __GNUC__ 949 if ((tmp->okey == skey) && (tmp->len == l)) { 950 if (!memcmp(tmp->name, name, l)) 951 return(tmp->name); 952 } 953 #else 954 if ((tmp->okey == skey) && (tmp->len == l) && 955 (!xmlStrncmp(tmp->name, name, l))) 956 return(tmp->name); 957 #endif 958 } 959 key = okey % dict->size; 960 } 961 962 ret = xmlDictAddString(dict, name, l); 963 if (ret == NULL) 964 return(NULL); 965 if (insert == NULL) { 966 entry = &(dict->dict[key]); 967 } else { 968 entry = xmlMalloc(sizeof(xmlDictEntry)); 969 if (entry == NULL) 970 return(NULL); 971 } 972 entry->name = ret; 973 entry->len = l; 974 entry->next = NULL; 975 entry->valid = 1; 976 entry->okey = okey; 977 978 979 if (insert != NULL) 980 insert->next = entry; 981 982 dict->nbElems++; 983 984 if ((nbi > MAX_HASH_LEN) && 985 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) { 986 if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0) 987 return(NULL); 988 } 989 /* Note that entry may have been freed at this point by xmlDictGrow */ 990 991 return(ret); 992 } 993 994 /** 995 * xmlDictExists: 996 * @dict: the dictionary 997 * @name: the name of the userdata 998 * @len: the length of the name, if -1 it is recomputed 999 * 1000 * Check if the @name exists in the dictionary @dict. 1001 * 1002 * Returns the internal copy of the name or NULL if not found. 1003 */ 1004 const xmlChar * 1005 xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) { 1006 unsigned long key, okey, nbi = 0; 1007 xmlDictEntryPtr insert; 1008 unsigned int l; 1009 1010 if ((dict == NULL) || (name == NULL)) 1011 return(NULL); 1012 1013 if (len < 0) 1014 l = strlen((const char *) name); 1015 else 1016 l = len; 1017 if (((dict->limit > 0) && (l >= dict->limit)) || 1018 (l > INT_MAX / 2)) 1019 return(NULL); 1020 1021 /* 1022 * Check for duplicate and insertion location. 1023 */ 1024 okey = xmlDictComputeKey(dict, name, l); 1025 key = okey % dict->size; 1026 if (dict->dict[key].valid == 0) { 1027 insert = NULL; 1028 } else { 1029 for (insert = &(dict->dict[key]); insert->next != NULL; 1030 insert = insert->next) { 1031 #ifdef __GNUC__ 1032 if ((insert->okey == okey) && (insert->len == l)) { 1033 if (!memcmp(insert->name, name, l)) 1034 return(insert->name); 1035 } 1036 #else 1037 if ((insert->okey == okey) && (insert->len == l) && 1038 (!xmlStrncmp(insert->name, name, l))) 1039 return(insert->name); 1040 #endif 1041 nbi++; 1042 } 1043 #ifdef __GNUC__ 1044 if ((insert->okey == okey) && (insert->len == l)) { 1045 if (!memcmp(insert->name, name, l)) 1046 return(insert->name); 1047 } 1048 #else 1049 if ((insert->okey == okey) && (insert->len == l) && 1050 (!xmlStrncmp(insert->name, name, l))) 1051 return(insert->name); 1052 #endif 1053 } 1054 1055 if (dict->subdict) { 1056 unsigned long skey; 1057 1058 /* we cannot always reuse the same okey for the subdict */ 1059 if (((dict->size == MIN_DICT_SIZE) && 1060 (dict->subdict->size != MIN_DICT_SIZE)) || 1061 ((dict->size != MIN_DICT_SIZE) && 1062 (dict->subdict->size == MIN_DICT_SIZE))) 1063 skey = xmlDictComputeKey(dict->subdict, name, l); 1064 else 1065 skey = okey; 1066 1067 key = skey % dict->subdict->size; 1068 if (dict->subdict->dict[key].valid != 0) { 1069 xmlDictEntryPtr tmp; 1070 1071 for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL; 1072 tmp = tmp->next) { 1073 #ifdef __GNUC__ 1074 if ((tmp->okey == skey) && (tmp->len == l)) { 1075 if (!memcmp(tmp->name, name, l)) 1076 return(tmp->name); 1077 } 1078 #else 1079 if ((tmp->okey == skey) && (tmp->len == l) && 1080 (!xmlStrncmp(tmp->name, name, l))) 1081 return(tmp->name); 1082 #endif 1083 nbi++; 1084 } 1085 #ifdef __GNUC__ 1086 if ((tmp->okey == skey) && (tmp->len == l)) { 1087 if (!memcmp(tmp->name, name, l)) 1088 return(tmp->name); 1089 } 1090 #else 1091 if ((tmp->okey == skey) && (tmp->len == l) && 1092 (!xmlStrncmp(tmp->name, name, l))) 1093 return(tmp->name); 1094 #endif 1095 } 1096 } 1097 1098 /* not found */ 1099 return(NULL); 1100 } 1101 1102 /** 1103 * xmlDictQLookup: 1104 * @dict: the dictionary 1105 * @prefix: the prefix 1106 * @name: the name 1107 * 1108 * Add the QName @prefix:@name to the hash @dict if not present. 1109 * 1110 * Returns the internal copy of the QName or NULL in case of internal error 1111 */ 1112 const xmlChar * 1113 xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) { 1114 unsigned long okey, key, nbi = 0; 1115 xmlDictEntryPtr entry; 1116 xmlDictEntryPtr insert; 1117 const xmlChar *ret; 1118 unsigned int len, plen, l; 1119 1120 if ((dict == NULL) || (name == NULL)) 1121 return(NULL); 1122 if (prefix == NULL) 1123 return(xmlDictLookup(dict, name, -1)); 1124 1125 l = len = strlen((const char *) name); 1126 plen = strlen((const char *) prefix); 1127 len += 1 + plen; 1128 1129 /* 1130 * Check for duplicate and insertion location. 1131 */ 1132 okey = xmlDictComputeQKey(dict, prefix, plen, name, l); 1133 key = okey % dict->size; 1134 if (dict->dict[key].valid == 0) { 1135 insert = NULL; 1136 } else { 1137 for (insert = &(dict->dict[key]); insert->next != NULL; 1138 insert = insert->next) { 1139 if ((insert->okey == okey) && (insert->len == len) && 1140 (xmlStrQEqual(prefix, name, insert->name))) 1141 return(insert->name); 1142 nbi++; 1143 } 1144 if ((insert->okey == okey) && (insert->len == len) && 1145 (xmlStrQEqual(prefix, name, insert->name))) 1146 return(insert->name); 1147 } 1148 1149 if (dict->subdict) { 1150 unsigned long skey; 1151 1152 /* we cannot always reuse the same okey for the subdict */ 1153 if (((dict->size == MIN_DICT_SIZE) && 1154 (dict->subdict->size != MIN_DICT_SIZE)) || 1155 ((dict->size != MIN_DICT_SIZE) && 1156 (dict->subdict->size == MIN_DICT_SIZE))) 1157 skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l); 1158 else 1159 skey = okey; 1160 1161 key = skey % dict->subdict->size; 1162 if (dict->subdict->dict[key].valid != 0) { 1163 xmlDictEntryPtr tmp; 1164 for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL; 1165 tmp = tmp->next) { 1166 if ((tmp->okey == skey) && (tmp->len == len) && 1167 (xmlStrQEqual(prefix, name, tmp->name))) 1168 return(tmp->name); 1169 nbi++; 1170 } 1171 if ((tmp->okey == skey) && (tmp->len == len) && 1172 (xmlStrQEqual(prefix, name, tmp->name))) 1173 return(tmp->name); 1174 } 1175 key = okey % dict->size; 1176 } 1177 1178 ret = xmlDictAddQString(dict, prefix, plen, name, l); 1179 if (ret == NULL) 1180 return(NULL); 1181 if (insert == NULL) { 1182 entry = &(dict->dict[key]); 1183 } else { 1184 entry = xmlMalloc(sizeof(xmlDictEntry)); 1185 if (entry == NULL) 1186 return(NULL); 1187 } 1188 entry->name = ret; 1189 entry->len = len; 1190 entry->next = NULL; 1191 entry->valid = 1; 1192 entry->okey = okey; 1193 1194 if (insert != NULL) 1195 insert->next = entry; 1196 1197 dict->nbElems++; 1198 1199 if ((nbi > MAX_HASH_LEN) && 1200 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) 1201 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size); 1202 /* Note that entry may have been freed at this point by xmlDictGrow */ 1203 1204 return(ret); 1205 } 1206 1207 /** 1208 * xmlDictOwns: 1209 * @dict: the dictionary 1210 * @str: the string 1211 * 1212 * check if a string is owned by the dictionary 1213 * 1214 * Returns 1 if true, 0 if false and -1 in case of error 1215 * -1 in case of error 1216 */ 1217 int 1218 xmlDictOwns(xmlDictPtr dict, const xmlChar *str) { 1219 xmlDictStringsPtr pool; 1220 1221 if ((dict == NULL) || (str == NULL)) 1222 return(-1); 1223 pool = dict->strings; 1224 while (pool != NULL) { 1225 if ((str >= &pool->array[0]) && (str <= pool->free)) 1226 return(1); 1227 pool = pool->next; 1228 } 1229 if (dict->subdict) 1230 return(xmlDictOwns(dict->subdict, str)); 1231 return(0); 1232 } 1233 1234 /** 1235 * xmlDictSize: 1236 * @dict: the dictionary 1237 * 1238 * Query the number of elements installed in the hash @dict. 1239 * 1240 * Returns the number of elements in the dictionary or 1241 * -1 in case of error 1242 */ 1243 int 1244 xmlDictSize(xmlDictPtr dict) { 1245 if (dict == NULL) 1246 return(-1); 1247 if (dict->subdict) 1248 return(dict->nbElems + dict->subdict->nbElems); 1249 return(dict->nbElems); 1250 } 1251 1252 /** 1253 * xmlDictSetLimit: 1254 * @dict: the dictionary 1255 * @limit: the limit in bytes 1256 * 1257 * Set a size limit for the dictionary 1258 * Added in 2.9.0 1259 * 1260 * Returns the previous limit of the dictionary or 0 1261 */ 1262 size_t 1263 xmlDictSetLimit(xmlDictPtr dict, size_t limit) { 1264 size_t ret; 1265 1266 if (dict == NULL) 1267 return(0); 1268 ret = dict->limit; 1269 dict->limit = limit; 1270 return(ret); 1271 } 1272 1273 /** 1274 * xmlDictGetUsage: 1275 * @dict: the dictionary 1276 * 1277 * Get how much memory is used by a dictionary for strings 1278 * Added in 2.9.0 1279 * 1280 * Returns the amount of strings allocated 1281 */ 1282 size_t 1283 xmlDictGetUsage(xmlDictPtr dict) { 1284 xmlDictStringsPtr pool; 1285 size_t limit = 0; 1286 1287 if (dict == NULL) 1288 return(0); 1289 pool = dict->strings; 1290 while (pool != NULL) { 1291 limit += pool->size; 1292 pool = pool->next; 1293 } 1294 return(limit); 1295 } 1296 1297 #define bottom_dict 1298 #include "elfgcchack.h" 1299