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