Home | History | Annotate | Download | only in libxml2
      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 (at) 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 (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 (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         case 9: value += name[8];
    457         case 8: value += name[7];
    458         case 7: value += name[6];
    459         case 6: value += name[5];
    460         case 5: value += name[4];
    461         case 4: value += name[3];
    462         case 3: value += name[2];
    463         case 2: value += name[1];
    464         default: break;
    465     }
    466     return(value);
    467 }
    468 
    469 /*
    470  * xmlDictComputeFastQKey:
    471  *
    472  * Calculate a hash key for two strings using a fast hash function
    473  * that works well for low hash table fill.
    474  *
    475  * Neither of the two strings must be NULL.
    476  */
    477 static unsigned long
    478 xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
    479                        const xmlChar *name, int len, int seed)
    480 {
    481     unsigned long value = (unsigned long) seed;
    482 
    483     if (plen == 0)
    484 	value += 30 * (unsigned long) ':';
    485     else
    486 	value += 30 * (*prefix);
    487 
    488     if (len > 10) {
    489         int offset = len - (plen + 1 + 1);
    490 	if (offset < 0)
    491 	    offset = len - (10 + 1);
    492 	value += name[offset];
    493         len = 10;
    494 	if (plen > 10)
    495 	    plen = 10;
    496     }
    497     switch (plen) {
    498         case 10: value += prefix[9];
    499         case 9: value += prefix[8];
    500         case 8: value += prefix[7];
    501         case 7: value += prefix[6];
    502         case 6: value += prefix[5];
    503         case 5: value += prefix[4];
    504         case 4: value += prefix[3];
    505         case 3: value += prefix[2];
    506         case 2: value += prefix[1];
    507         case 1: value += prefix[0];
    508         default: break;
    509     }
    510     len -= plen;
    511     if (len > 0) {
    512         value += (unsigned long) ':';
    513 	len--;
    514     }
    515     switch (len) {
    516         case 10: value += name[9];
    517         case 9: value += name[8];
    518         case 8: value += name[7];
    519         case 7: value += name[6];
    520         case 6: value += name[5];
    521         case 5: value += name[4];
    522         case 4: value += name[3];
    523         case 3: value += name[2];
    524         case 2: value += name[1];
    525         case 1: value += name[0];
    526         default: break;
    527     }
    528     return(value);
    529 }
    530 
    531 /**
    532  * xmlDictCreate:
    533  *
    534  * Create a new dictionary
    535  *
    536  * Returns the newly created dictionary, or NULL if an error occured.
    537  */
    538 xmlDictPtr
    539 xmlDictCreate(void) {
    540     xmlDictPtr dict;
    541 
    542     if (!xmlDictInitialized)
    543         if (!__xmlInitializeDict())
    544             return(NULL);
    545 
    546 #ifdef DICT_DEBUG_PATTERNS
    547     fprintf(stderr, "C");
    548 #endif
    549 
    550     dict = xmlMalloc(sizeof(xmlDict));
    551     if (dict) {
    552         dict->ref_counter = 1;
    553         dict->limit = 0;
    554 
    555         dict->size = MIN_DICT_SIZE;
    556 	dict->nbElems = 0;
    557         dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
    558 	dict->strings = NULL;
    559 	dict->subdict = NULL;
    560         if (dict->dict) {
    561 	    memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
    562 #ifdef DICT_RANDOMIZATION
    563             dict->seed = __xmlRandom();
    564 #else
    565             dict->seed = 0;
    566 #endif
    567 	    return(dict);
    568         }
    569         xmlFree(dict);
    570     }
    571     return(NULL);
    572 }
    573 
    574 /**
    575  * xmlDictCreateSub:
    576  * @sub: an existing dictionary
    577  *
    578  * Create a new dictionary, inheriting strings from the read-only
    579  * dictionary @sub. On lookup, strings are first searched in the
    580  * new dictionary, then in @sub, and if not found are created in the
    581  * new dictionary.
    582  *
    583  * Returns the newly created dictionary, or NULL if an error occured.
    584  */
    585 xmlDictPtr
    586 xmlDictCreateSub(xmlDictPtr sub) {
    587     xmlDictPtr dict = xmlDictCreate();
    588 
    589     if ((dict != NULL) && (sub != NULL)) {
    590 #ifdef DICT_DEBUG_PATTERNS
    591         fprintf(stderr, "R");
    592 #endif
    593         dict->seed = sub->seed;
    594         dict->subdict = sub;
    595 	xmlDictReference(dict->subdict);
    596     }
    597     return(dict);
    598 }
    599 
    600 /**
    601  * xmlDictReference:
    602  * @dict: the dictionary
    603  *
    604  * Increment the reference counter of a dictionary
    605  *
    606  * Returns 0 in case of success and -1 in case of error
    607  */
    608 int
    609 xmlDictReference(xmlDictPtr dict) {
    610     if (!xmlDictInitialized)
    611         if (!__xmlInitializeDict())
    612             return(-1);
    613 
    614     if (dict == NULL) return -1;
    615     xmlRMutexLock(xmlDictMutex);
    616     dict->ref_counter++;
    617     xmlRMutexUnlock(xmlDictMutex);
    618     return(0);
    619 }
    620 
    621 /**
    622  * xmlDictGrow:
    623  * @dict: the dictionary
    624  * @size: the new size of the dictionary
    625  *
    626  * resize the dictionary
    627  *
    628  * Returns 0 in case of success, -1 in case of failure
    629  */
    630 static int
    631 xmlDictGrow(xmlDictPtr dict, size_t size) {
    632     unsigned long key, okey;
    633     size_t oldsize, i;
    634     xmlDictEntryPtr iter, next;
    635     struct _xmlDictEntry *olddict;
    636 #ifdef DEBUG_GROW
    637     unsigned long nbElem = 0;
    638 #endif
    639     int ret = 0;
    640     int keep_keys = 1;
    641 
    642     if (dict == NULL)
    643 	return(-1);
    644     if (size < 8)
    645         return(-1);
    646     if (size > 8 * 2048)
    647 	return(-1);
    648 
    649 #ifdef DICT_DEBUG_PATTERNS
    650     fprintf(stderr, "*");
    651 #endif
    652 
    653     oldsize = dict->size;
    654     olddict = dict->dict;
    655     if (olddict == NULL)
    656         return(-1);
    657     if (oldsize == MIN_DICT_SIZE)
    658         keep_keys = 0;
    659 
    660     dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
    661     if (dict->dict == NULL) {
    662 	dict->dict = olddict;
    663 	return(-1);
    664     }
    665     memset(dict->dict, 0, size * sizeof(xmlDictEntry));
    666     dict->size = size;
    667 
    668     /*	If the two loops are merged, there would be situations where
    669 	a new entry needs to allocated and data copied into it from
    670 	the main dict. It is nicer to run through the array twice, first
    671 	copying all the elements in the main array (less probability of
    672 	allocate) and then the rest, so we only free in the second loop.
    673     */
    674     for (i = 0; i < oldsize; i++) {
    675 	if (olddict[i].valid == 0)
    676 	    continue;
    677 
    678 	if (keep_keys)
    679 	    okey = olddict[i].okey;
    680 	else
    681 	    okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
    682 	key = okey % dict->size;
    683 
    684 	if (dict->dict[key].valid == 0) {
    685 	    memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
    686 	    dict->dict[key].next = NULL;
    687 	    dict->dict[key].okey = okey;
    688 	} else {
    689 	    xmlDictEntryPtr entry;
    690 
    691 	    entry = xmlMalloc(sizeof(xmlDictEntry));
    692 	    if (entry != NULL) {
    693 		entry->name = olddict[i].name;
    694 		entry->len = olddict[i].len;
    695 		entry->okey = okey;
    696 		entry->next = dict->dict[key].next;
    697 		entry->valid = 1;
    698 		dict->dict[key].next = entry;
    699 	    } else {
    700 	        /*
    701 		 * we don't have much ways to alert from herei
    702 		 * result is loosing an entry and unicity garantee
    703 		 */
    704 	        ret = -1;
    705 	    }
    706 	}
    707 #ifdef DEBUG_GROW
    708 	nbElem++;
    709 #endif
    710     }
    711 
    712     for (i = 0; i < oldsize; i++) {
    713 	iter = olddict[i].next;
    714 	while (iter) {
    715 	    next = iter->next;
    716 
    717 	    /*
    718 	     * put back the entry in the new dict
    719 	     */
    720 
    721 	    if (keep_keys)
    722 		okey = iter->okey;
    723 	    else
    724 		okey = xmlDictComputeKey(dict, iter->name, iter->len);
    725 	    key = okey % dict->size;
    726 	    if (dict->dict[key].valid == 0) {
    727 		memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
    728 		dict->dict[key].next = NULL;
    729 		dict->dict[key].valid = 1;
    730 		dict->dict[key].okey = okey;
    731 		xmlFree(iter);
    732 	    } else {
    733 		iter->next = dict->dict[key].next;
    734 		iter->okey = okey;
    735 		dict->dict[key].next = iter;
    736 	    }
    737 
    738 #ifdef DEBUG_GROW
    739 	    nbElem++;
    740 #endif
    741 
    742 	    iter = next;
    743 	}
    744     }
    745 
    746     xmlFree(olddict);
    747 
    748 #ifdef DEBUG_GROW
    749     xmlGenericError(xmlGenericErrorContext,
    750 	    "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
    751 #endif
    752 
    753     return(ret);
    754 }
    755 
    756 /**
    757  * xmlDictFree:
    758  * @dict: the dictionary
    759  *
    760  * Free the hash @dict and its contents. The userdata is
    761  * deallocated with @f if provided.
    762  */
    763 void
    764 xmlDictFree(xmlDictPtr dict) {
    765     size_t i;
    766     xmlDictEntryPtr iter;
    767     xmlDictEntryPtr next;
    768     int inside_dict = 0;
    769     xmlDictStringsPtr pool, nextp;
    770 
    771     if (dict == NULL)
    772 	return;
    773 
    774     if (!xmlDictInitialized)
    775         if (!__xmlInitializeDict())
    776             return;
    777 
    778     /* decrement the counter, it may be shared by a parser and docs */
    779     xmlRMutexLock(xmlDictMutex);
    780     dict->ref_counter--;
    781     if (dict->ref_counter > 0) {
    782         xmlRMutexUnlock(xmlDictMutex);
    783         return;
    784     }
    785 
    786     xmlRMutexUnlock(xmlDictMutex);
    787 
    788     if (dict->subdict != NULL) {
    789         xmlDictFree(dict->subdict);
    790     }
    791 
    792     if (dict->dict) {
    793 	for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
    794 	    iter = &(dict->dict[i]);
    795 	    if (iter->valid == 0)
    796 		continue;
    797 	    inside_dict = 1;
    798 	    while (iter) {
    799 		next = iter->next;
    800 		if (!inside_dict)
    801 		    xmlFree(iter);
    802 		dict->nbElems--;
    803 		inside_dict = 0;
    804 		iter = next;
    805 	    }
    806 	}
    807 	xmlFree(dict->dict);
    808     }
    809     pool = dict->strings;
    810     while (pool != NULL) {
    811         nextp = pool->next;
    812 	xmlFree(pool);
    813 	pool = nextp;
    814     }
    815     xmlFree(dict);
    816 }
    817 
    818 /**
    819  * xmlDictLookup:
    820  * @dict: the dictionary
    821  * @name: the name of the userdata
    822  * @len: the length of the name, if -1 it is recomputed
    823  *
    824  * Add the @name to the dictionary @dict if not present.
    825  *
    826  * Returns the internal copy of the name or NULL in case of internal error
    827  */
    828 const xmlChar *
    829 xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
    830     unsigned long key, okey, nbi = 0;
    831     xmlDictEntryPtr entry;
    832     xmlDictEntryPtr insert;
    833     const xmlChar *ret;
    834     unsigned int l;
    835 
    836     if ((dict == NULL) || (name == NULL))
    837 	return(NULL);
    838 
    839     if (len < 0)
    840         l = strlen((const char *) name);
    841     else
    842         l = len;
    843 
    844     if (((dict->limit > 0) && (l >= dict->limit)) ||
    845         (l > INT_MAX / 2))
    846         return(NULL);
    847 
    848     /*
    849      * Check for duplicate and insertion location.
    850      */
    851     okey = xmlDictComputeKey(dict, name, l);
    852     key = okey % dict->size;
    853     if (dict->dict[key].valid == 0) {
    854 	insert = NULL;
    855     } else {
    856 	for (insert = &(dict->dict[key]); insert->next != NULL;
    857 	     insert = insert->next) {
    858 #ifdef __GNUC__
    859 	    if ((insert->okey == okey) && (insert->len == l)) {
    860 		if (!memcmp(insert->name, name, l))
    861 		    return(insert->name);
    862 	    }
    863 #else
    864 	    if ((insert->okey == okey) && (insert->len == l) &&
    865 	        (!xmlStrncmp(insert->name, name, l)))
    866 		return(insert->name);
    867 #endif
    868 	    nbi++;
    869 	}
    870 #ifdef __GNUC__
    871 	if ((insert->okey == okey) && (insert->len == l)) {
    872 	    if (!memcmp(insert->name, name, l))
    873 		return(insert->name);
    874 	}
    875 #else
    876 	if ((insert->okey == okey) && (insert->len == l) &&
    877 	    (!xmlStrncmp(insert->name, name, l)))
    878 	    return(insert->name);
    879 #endif
    880     }
    881 
    882     if (dict->subdict) {
    883         unsigned long skey;
    884 
    885         /* we cannot always reuse the same okey for the subdict */
    886         if (((dict->size == MIN_DICT_SIZE) &&
    887 	     (dict->subdict->size != MIN_DICT_SIZE)) ||
    888             ((dict->size != MIN_DICT_SIZE) &&
    889 	     (dict->subdict->size == MIN_DICT_SIZE)))
    890 	    skey = xmlDictComputeKey(dict->subdict, name, l);
    891 	else
    892 	    skey = okey;
    893 
    894 	key = skey % dict->subdict->size;
    895 	if (dict->subdict->dict[key].valid != 0) {
    896 	    xmlDictEntryPtr tmp;
    897 
    898 	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
    899 		 tmp = tmp->next) {
    900 #ifdef __GNUC__
    901 		if ((tmp->okey == skey) && (tmp->len == l)) {
    902 		    if (!memcmp(tmp->name, name, l))
    903 			return(tmp->name);
    904 		}
    905 #else
    906 		if ((tmp->okey == skey) && (tmp->len == l) &&
    907 		    (!xmlStrncmp(tmp->name, name, l)))
    908 		    return(tmp->name);
    909 #endif
    910 		nbi++;
    911 	    }
    912 #ifdef __GNUC__
    913 	    if ((tmp->okey == skey) && (tmp->len == l)) {
    914 		if (!memcmp(tmp->name, name, l))
    915 		    return(tmp->name);
    916 	    }
    917 #else
    918 	    if ((tmp->okey == skey) && (tmp->len == l) &&
    919 		(!xmlStrncmp(tmp->name, name, l)))
    920 		return(tmp->name);
    921 #endif
    922 	}
    923 	key = okey % dict->size;
    924     }
    925 
    926     ret = xmlDictAddString(dict, name, l);
    927     if (ret == NULL)
    928         return(NULL);
    929     if (insert == NULL) {
    930 	entry = &(dict->dict[key]);
    931     } else {
    932 	entry = xmlMalloc(sizeof(xmlDictEntry));
    933 	if (entry == NULL)
    934 	     return(NULL);
    935     }
    936     entry->name = ret;
    937     entry->len = l;
    938     entry->next = NULL;
    939     entry->valid = 1;
    940     entry->okey = okey;
    941 
    942 
    943     if (insert != NULL)
    944 	insert->next = entry;
    945 
    946     dict->nbElems++;
    947 
    948     if ((nbi > MAX_HASH_LEN) &&
    949         (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
    950 	if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
    951 	    return(NULL);
    952     }
    953     /* Note that entry may have been freed at this point by xmlDictGrow */
    954 
    955     return(ret);
    956 }
    957 
    958 /**
    959  * xmlDictExists:
    960  * @dict: the dictionary
    961  * @name: the name of the userdata
    962  * @len: the length of the name, if -1 it is recomputed
    963  *
    964  * Check if the @name exists in the dictionary @dict.
    965  *
    966  * Returns the internal copy of the name or NULL if not found.
    967  */
    968 const xmlChar *
    969 xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
    970     unsigned long key, okey, nbi = 0;
    971     xmlDictEntryPtr insert;
    972     unsigned int l;
    973 
    974     if ((dict == NULL) || (name == NULL))
    975 	return(NULL);
    976 
    977     if (len < 0)
    978         l = strlen((const char *) name);
    979     else
    980         l = len;
    981     if (((dict->limit > 0) && (l >= dict->limit)) ||
    982         (l > INT_MAX / 2))
    983         return(NULL);
    984 
    985     /*
    986      * Check for duplicate and insertion location.
    987      */
    988     okey = xmlDictComputeKey(dict, name, l);
    989     key = okey % dict->size;
    990     if (dict->dict[key].valid == 0) {
    991 	insert = NULL;
    992     } else {
    993 	for (insert = &(dict->dict[key]); insert->next != NULL;
    994 	     insert = insert->next) {
    995 #ifdef __GNUC__
    996 	    if ((insert->okey == okey) && (insert->len == l)) {
    997 		if (!memcmp(insert->name, name, l))
    998 		    return(insert->name);
    999 	    }
   1000 #else
   1001 	    if ((insert->okey == okey) && (insert->len == l) &&
   1002 	        (!xmlStrncmp(insert->name, name, l)))
   1003 		return(insert->name);
   1004 #endif
   1005 	    nbi++;
   1006 	}
   1007 #ifdef __GNUC__
   1008 	if ((insert->okey == okey) && (insert->len == l)) {
   1009 	    if (!memcmp(insert->name, name, l))
   1010 		return(insert->name);
   1011 	}
   1012 #else
   1013 	if ((insert->okey == okey) && (insert->len == l) &&
   1014 	    (!xmlStrncmp(insert->name, name, l)))
   1015 	    return(insert->name);
   1016 #endif
   1017     }
   1018 
   1019     if (dict->subdict) {
   1020         unsigned long skey;
   1021 
   1022         /* we cannot always reuse the same okey for the subdict */
   1023         if (((dict->size == MIN_DICT_SIZE) &&
   1024 	     (dict->subdict->size != MIN_DICT_SIZE)) ||
   1025             ((dict->size != MIN_DICT_SIZE) &&
   1026 	     (dict->subdict->size == MIN_DICT_SIZE)))
   1027 	    skey = xmlDictComputeKey(dict->subdict, name, l);
   1028 	else
   1029 	    skey = okey;
   1030 
   1031 	key = skey % dict->subdict->size;
   1032 	if (dict->subdict->dict[key].valid != 0) {
   1033 	    xmlDictEntryPtr tmp;
   1034 
   1035 	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
   1036 		 tmp = tmp->next) {
   1037 #ifdef __GNUC__
   1038 		if ((tmp->okey == skey) && (tmp->len == l)) {
   1039 		    if (!memcmp(tmp->name, name, l))
   1040 			return(tmp->name);
   1041 		}
   1042 #else
   1043 		if ((tmp->okey == skey) && (tmp->len == l) &&
   1044 		    (!xmlStrncmp(tmp->name, name, l)))
   1045 		    return(tmp->name);
   1046 #endif
   1047 		nbi++;
   1048 	    }
   1049 #ifdef __GNUC__
   1050 	    if ((tmp->okey == skey) && (tmp->len == l)) {
   1051 		if (!memcmp(tmp->name, name, l))
   1052 		    return(tmp->name);
   1053 	    }
   1054 #else
   1055 	    if ((tmp->okey == skey) && (tmp->len == l) &&
   1056 		(!xmlStrncmp(tmp->name, name, l)))
   1057 		return(tmp->name);
   1058 #endif
   1059 	}
   1060     }
   1061 
   1062     /* not found */
   1063     return(NULL);
   1064 }
   1065 
   1066 /**
   1067  * xmlDictQLookup:
   1068  * @dict: the dictionary
   1069  * @prefix: the prefix
   1070  * @name: the name
   1071  *
   1072  * Add the QName @prefix:@name to the hash @dict if not present.
   1073  *
   1074  * Returns the internal copy of the QName or NULL in case of internal error
   1075  */
   1076 const xmlChar *
   1077 xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
   1078     unsigned long okey, key, nbi = 0;
   1079     xmlDictEntryPtr entry;
   1080     xmlDictEntryPtr insert;
   1081     const xmlChar *ret;
   1082     unsigned int len, plen, l;
   1083 
   1084     if ((dict == NULL) || (name == NULL))
   1085 	return(NULL);
   1086     if (prefix == NULL)
   1087         return(xmlDictLookup(dict, name, -1));
   1088 
   1089     l = len = strlen((const char *) name);
   1090     plen = strlen((const char *) prefix);
   1091     len += 1 + plen;
   1092 
   1093     /*
   1094      * Check for duplicate and insertion location.
   1095      */
   1096     okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
   1097     key = okey % dict->size;
   1098     if (dict->dict[key].valid == 0) {
   1099 	insert = NULL;
   1100     } else {
   1101 	for (insert = &(dict->dict[key]); insert->next != NULL;
   1102 	     insert = insert->next) {
   1103 	    if ((insert->okey == okey) && (insert->len == len) &&
   1104 	        (xmlStrQEqual(prefix, name, insert->name)))
   1105 		return(insert->name);
   1106 	    nbi++;
   1107 	}
   1108 	if ((insert->okey == okey) && (insert->len == len) &&
   1109 	    (xmlStrQEqual(prefix, name, insert->name)))
   1110 	    return(insert->name);
   1111     }
   1112 
   1113     if (dict->subdict) {
   1114         unsigned long skey;
   1115 
   1116         /* we cannot always reuse the same okey for the subdict */
   1117         if (((dict->size == MIN_DICT_SIZE) &&
   1118 	     (dict->subdict->size != MIN_DICT_SIZE)) ||
   1119             ((dict->size != MIN_DICT_SIZE) &&
   1120 	     (dict->subdict->size == MIN_DICT_SIZE)))
   1121 	    skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
   1122 	else
   1123 	    skey = okey;
   1124 
   1125 	key = skey % dict->subdict->size;
   1126 	if (dict->subdict->dict[key].valid != 0) {
   1127 	    xmlDictEntryPtr tmp;
   1128 	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
   1129 		 tmp = tmp->next) {
   1130 		if ((tmp->okey == skey) && (tmp->len == len) &&
   1131 		    (xmlStrQEqual(prefix, name, tmp->name)))
   1132 		    return(tmp->name);
   1133 		nbi++;
   1134 	    }
   1135 	    if ((tmp->okey == skey) && (tmp->len == len) &&
   1136 		(xmlStrQEqual(prefix, name, tmp->name)))
   1137 		return(tmp->name);
   1138 	}
   1139 	key = okey % dict->size;
   1140     }
   1141 
   1142     ret = xmlDictAddQString(dict, prefix, plen, name, l);
   1143     if (ret == NULL)
   1144         return(NULL);
   1145     if (insert == NULL) {
   1146 	entry = &(dict->dict[key]);
   1147     } else {
   1148 	entry = xmlMalloc(sizeof(xmlDictEntry));
   1149 	if (entry == NULL)
   1150 	     return(NULL);
   1151     }
   1152     entry->name = ret;
   1153     entry->len = len;
   1154     entry->next = NULL;
   1155     entry->valid = 1;
   1156     entry->okey = okey;
   1157 
   1158     if (insert != NULL)
   1159 	insert->next = entry;
   1160 
   1161     dict->nbElems++;
   1162 
   1163     if ((nbi > MAX_HASH_LEN) &&
   1164         (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
   1165 	xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
   1166     /* Note that entry may have been freed at this point by xmlDictGrow */
   1167 
   1168     return(ret);
   1169 }
   1170 
   1171 /**
   1172  * xmlDictOwns:
   1173  * @dict: the dictionary
   1174  * @str: the string
   1175  *
   1176  * check if a string is owned by the disctionary
   1177  *
   1178  * Returns 1 if true, 0 if false and -1 in case of error
   1179  * -1 in case of error
   1180  */
   1181 int
   1182 xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
   1183     xmlDictStringsPtr pool;
   1184 
   1185     if ((dict == NULL) || (str == NULL))
   1186 	return(-1);
   1187     pool = dict->strings;
   1188     while (pool != NULL) {
   1189         if ((str >= &pool->array[0]) && (str <= pool->free))
   1190 	    return(1);
   1191 	pool = pool->next;
   1192     }
   1193     if (dict->subdict)
   1194         return(xmlDictOwns(dict->subdict, str));
   1195     return(0);
   1196 }
   1197 
   1198 /**
   1199  * xmlDictSize:
   1200  * @dict: the dictionary
   1201  *
   1202  * Query the number of elements installed in the hash @dict.
   1203  *
   1204  * Returns the number of elements in the dictionary or
   1205  * -1 in case of error
   1206  */
   1207 int
   1208 xmlDictSize(xmlDictPtr dict) {
   1209     if (dict == NULL)
   1210 	return(-1);
   1211     if (dict->subdict)
   1212         return(dict->nbElems + dict->subdict->nbElems);
   1213     return(dict->nbElems);
   1214 }
   1215 
   1216 /**
   1217  * xmlDictSetLimit:
   1218  * @dict: the dictionary
   1219  * @limit: the limit in bytes
   1220  *
   1221  * Set a size limit for the dictionary
   1222  * Added in 2.9.0
   1223  *
   1224  * Returns the previous limit of the dictionary or 0
   1225  */
   1226 size_t
   1227 xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
   1228     size_t ret;
   1229 
   1230     if (dict == NULL)
   1231 	return(0);
   1232     ret = dict->limit;
   1233     dict->limit = limit;
   1234     return(ret);
   1235 }
   1236 
   1237 /**
   1238  * xmlDictGetUsage:
   1239  * @dict: the dictionary
   1240  *
   1241  * Get how much memory is used by a dictionary for strings
   1242  * Added in 2.9.0
   1243  *
   1244  * Returns the amount of strings allocated
   1245  */
   1246 size_t
   1247 xmlDictGetUsage(xmlDictPtr dict) {
   1248     xmlDictStringsPtr pool;
   1249     size_t limit = 0;
   1250 
   1251     if (dict == NULL)
   1252 	return(0);
   1253     pool = dict->strings;
   1254     while (pool != NULL) {
   1255         limit += pool->size;
   1256 	pool = pool->next;
   1257     }
   1258     return(limit);
   1259 }
   1260 
   1261 #define bottom_dict
   1262 #include "elfgcchack.h"
   1263