xref: /reactos/sdk/lib/3rdparty/libxml2/dict.c (revision 53221834)
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