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