diff -r 000000000000 -r e35f40988205 xml/libxml2libs/src/libxml2/libxml2_dict.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xml/libxml2libs/src/libxml2/libxml2_dict.c Thu Dec 17 09:29:21 2009 +0200 @@ -0,0 +1,763 @@ +/* + * libxml2_dict.c: dictionary of reusable strings, just used to avoid allocation + * and freeing operations. + * + * Copyright (C) 2003 Daniel Veillard. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + * Author: daniel@veillard.com + * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. + */ + +#define IN_LIBXML +#undef XE_ENABLE_GS_CACHING +#include "xmlenglibxml.h" + +#include +#include + +#define MAX_HASH_LEN 4 + + +#define MIN_DICT_SIZE 128 +#define MAX_DICT_HASH 8 * 2048 + +/* #define ALLOW_REMOVAL */ +/* #define DEBUG_GROW */ + +/* + * xmlDictAddString: + * @param dict the dictionnary + * @param name the name of the userdata + * @param len the length of the name, if -1 it is recomputed + * + * Add the string to the array[s] + * + * Returns the pointer of the local string, or NULL in case of error. + * + * OOM: possible --> NULL is returned, OOM flag is set + */ +static const xmlChar* +xmlDictAddString(xmlDictPtr dict, const xmlChar *name, int namelen) { + xmlDictStringsPtr pool; + const xmlChar *ret; + int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ + + pool = dict->strings; + while (pool) { + if (pool->end - pool->free > namelen) + goto found_pool; + if (pool->size > size) size = pool->size; + pool = pool->next; + } + /* + * Not found, need to allocate + */ + if (!pool) { + if (size == 0) + size = 1000; + else + size *= 4; /* exponential growth */ + + if (size < 4 * namelen) + size = 4 * namelen; /* just in case ! */ + + pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); + if (!pool) + return(NULL); // OOM + pool->size = size; + pool->nbStrings = 0; + pool->free = &pool->array[0]; + pool->end = &pool->array[size]; + pool->next = dict->strings; + dict->strings = pool; + } +found_pool: + ret = pool->free; + memcpy(pool->free, name, namelen); + pool->free += namelen; + *(pool->free++) = 0; + return(ret); +} + +/* + * xmlDictAddQString: + * @param dict the dictionnary + * @param prefix the prefix of the userdata + * @param name the name of the userdata + * @param len the length of the name, if -1 it is recomputed + * + * Add the QName to the array[s] + * + * Returns the pointer of the local string, or NULL in case of error. + */ +static const xmlChar * +xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, + const xmlChar *name, int namelen) +{ + xmlDictStringsPtr pool; + const xmlChar *ret; + int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ + int plen; + + if (!prefix) + return(xmlDictAddString(dict, name, namelen)); + plen = xmlStrlen(prefix); + + pool = dict->strings; + while (pool) { + if (pool->end - pool->free > namelen) + goto found_pool; + if (pool->size > size) + size = pool->size; + pool = pool->next; + } + /* + * Not found, need to allocate + */ + if (!pool) { + if (size == 0) + size = 1000; + else + size *= 4; /* exponential growth */ + if (size < 4 * namelen) + size = 4 * namelen; /* just in case ! */ + pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); + if (!pool) + return(NULL); + pool->size = size; + pool->nbStrings = 0; + pool->free = &pool->array[0]; + pool->end = &pool->array[size]; + pool->next = dict->strings; + dict->strings = pool; + } +found_pool: + ret = pool->free; + memcpy(pool->free, prefix, plen); + pool->free += plen; + *(pool->free++) = ':'; + namelen -= plen + 1; + memcpy(pool->free, name, namelen); + pool->free += namelen; + *(pool->free++) = 0; + return(ret); +} + +/* + * xmlDictComputeKey: + * Calculate the hash key + * + * OOM: never + */ + + +static unsigned long +xmlDictComputeKey(const xmlChar *name, int namelen) { + unsigned long value = 0L; + + if (!name) + return(0); + value = *name; + value <<= 5; + if (namelen > 10) { + value += name[namelen - 1]; + namelen = 10; + } + // DONE: OPTIMIZE: Will it be better to rewrite SWITCH as IF? : + // if(namelen > 1) // namelen by this point is <= 10 + // value+=name[namelen-1] + // + if (namelen > 1) + value += name[namelen-1]; + /* + switch (namelen) { + case 10: value += name[9]; + case 9: value += name[8]; + case 8: value += name[7]; + case 7: value += name[6]; + case 6: value += name[5]; + case 5: value += name[4]; + case 4: value += name[3]; + case 3: value += name[2]; + case 2: value += name[1]; + default: break; + } + */ + return(value); +} + +/* + * xmlDictComputeQKey: + * Calculate the hash key + */ +static unsigned long +xmlDictComputeQKey(const xmlChar *prefix, const xmlChar *name, int len) +{ + unsigned long value; // = 0L; //avoided initialization + int plen; + + if (!prefix) + return(xmlDictComputeKey(name, len)); + + plen = xmlStrlen(prefix); + value = 30 * (plen ? *prefix : (unsigned long) ':'); + + if (len > 10) { + value += name[len - (plen + 1 + 1)]; + len = 10; + if (plen > 10) + plen = 10; + } + + // DONE: OPTIMIZE: see "Optimize-It" note in xmlDictComputeKey + /* + switch (plen) { + case 10: value += prefix[9]; + case 9: value += prefix[8]; + case 8: value += prefix[7]; + case 7: value += prefix[6]; + case 6: value += prefix[5]; + case 5: value += prefix[4]; + case 4: value += prefix[3]; + case 3: value += prefix[2]; + case 2: value += prefix[1]; + case 1: value += prefix[0]; + default: break; + } + */ + // REPLACED WITH + if (plen < 11 && plen >0) + value += prefix[plen-1]; + //------------------------------------- + len -= plen; + if (len > 0) { + value += (unsigned long) ':'; + len--; + } + /* --- REPLACED + switch (len) { + case 10: value += name[9]; + case 9: value += name[8]; + case 8: value += name[7]; + case 7: value += name[6]; + case 6: value += name[5]; + case 5: value += name[4]; + case 4: value += name[3]; + case 3: value += name[2]; + case 2: value += name[1]; + case 1: value += name[0]; + default: break; + } + */ + //-- REPLACED WITH + if(len > 0 && len < 11) + value += name[len-1]; + //-------------------- + return(value); +} + +/** + * xmlDictCreate: + * + * Create a new dictionary + * + * Returns the newly created dictionnary, or NULL if an error occured. + * + * OOM: possible --> NULL is returned; OOM flag is set + */ +XMLPUBFUNEXPORT xmlDictPtr +xmlDictCreate(void) { + xmlDictPtr dict; + + dict = (xmlDictPtr)xmlMalloc(sizeof(xmlDict)); + if (dict) { + dict->ref_counter = 1; + + dict->size = MIN_DICT_SIZE; + dict->nbElems = 0; + dict->dict = (xmlDictEntryPtr)xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry)); + dict->strings = NULL; + dict->subdict = NULL; + if (dict->dict) { + memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry)); + return(dict); + } + xmlFree(dict); // OOM + } + return(NULL); // OOM +} + +/** + * xmlDictCreateSub: + * @param sub an existing dictionnary + * + * Create a new dictionary, inheriting strings from the read-only + * dictionnary sub. On lookup, strings are first searched in the + * new dictionnary, then in sub, and if not found are created in the + * new dictionnary. + * + * Returns the newly created dictionnary, or NULL if an error occured. + */ +XMLPUBFUNEXPORT xmlDictPtr +xmlDictCreateSub(xmlDictPtr sub) { + xmlDictPtr dict = xmlDictCreate(); + + if (dict && sub) { + dict->subdict = sub; + xmlDictReference(dict->subdict); + } + return(dict); +} + +/** + * xmlDictReference: + * @param dict the dictionnary + * + * Increment the reference counter of a dictionary + * + * Returns 0 in case of success and -1 in case of error + * + * OOM: never + */ +XMLPUBFUNEXPORT int +xmlDictReference(xmlDictPtr dict) { + if (!dict) + return -1; + dict->ref_counter++; + return(0); +} + +/** + * xmlDictGrow: + * @param dict the dictionnary + * @param size the new size of the dictionnary + * + * resize the dictionnary + * + * Returns 0 in case of success, -1 in case of failure + * + * OOM: possible --> returns -1, OOM is set + */ +static int +xmlDictGrow(xmlDictPtr dict, int size) { + unsigned long key; + int oldsize, i; + xmlDictEntryPtr iter, next; + struct _xmlDictEntry *olddict; +#ifdef DEBUG_GROW + unsigned long nbElem = 0; +#endif + + if (!dict || size < 8 || size > 8 * 2048) + return(-1); + + oldsize = dict->size; + olddict = dict->dict; + if (!olddict) + return(-1); + + dict->dict = (xmlDictEntryPtr)xmlMalloc(size * sizeof(xmlDictEntry)); + if (!dict->dict) { + dict->dict = olddict; + return(-1); + } + memset(dict->dict, 0, size * sizeof(xmlDictEntry)); + dict->size = size; + + /* If the two loops are merged, there would be situations where + a new entry needs to allocated and data copied into it from + the main dict. So instead, we run through the array twice, first + copying all the elements in the main array (where we can't get + conflicts) and then the rest, so we only free (and don't allocate) + */ + for (i = 0; i < oldsize; i++) + { + if (olddict[i].valid == 0) + continue; + key = xmlDictComputeKey(olddict[i].name, olddict[i].len) % dict->size; + memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry)); + dict->dict[key].next = NULL; +#ifdef DEBUG_GROW + nbElem++; +#endif + } + + for (i = 0; i < oldsize; i++) + { + iter = olddict[i].next; + while (iter) { + next = iter->next; + /* + * put back the entry in the new dict + */ + key = xmlDictComputeKey(iter->name, iter->len) % dict->size; + if (dict->dict[key].valid == 0) { + memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry)); + dict->dict[key].next = NULL; + dict->dict[key].valid = 1; + xmlFree(iter); + } else { + iter->next = dict->dict[key].next; + dict->dict[key].next = iter; + } +#ifdef DEBUG_GROW + nbElem++; +#endif + iter = next; + } // while (iter) + } // for (i = 0; i < oldsize; i++) + + xmlFree(olddict); + +#ifdef DEBUG_GROW + xmlGenericError(xmlGenericErrorContext, + "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem); +#endif + + return(0); +} + +/** + * xmlDictFree: + * @param dict the dictionnary + * + * Free the hash dict and its contents. The userdata is + * deallocated with f if provided. + */ +XMLPUBFUNEXPORT void +xmlDictFree(xmlDictPtr dict) +{ + int i; + xmlDictEntryPtr iter; + xmlDictEntryPtr next; + int inside_dict = 0; + xmlDictStringsPtr pool, nextp; + + if (!dict) + return; + + /* decrement the counter, it may be shared by a parser and docs */ + dict->ref_counter--; + if (dict->ref_counter > 0) + return; + + if (dict->subdict) { + xmlDictFree(dict->subdict); + } + + if (dict->dict) { + for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) + { + iter = &(dict->dict[i]); + if (iter->valid == 0) + continue; + inside_dict = 1; + while (iter) + { + next = iter->next; + if (!inside_dict) + xmlFree(iter); + dict->nbElems--; + inside_dict = 0; + iter = next; + } + inside_dict = 0; + } + xmlFree(dict->dict); + } + + pool = dict->strings; + while (pool) { + nextp = pool->next; + xmlFree(pool); + pool = nextp; + } + xmlFree(dict); +} + +/** + * xmlDictLookup: + * @param dict the dictionnary + * @param name the name of the userdata + * @param len the length of the name, if -1 it is recomputed + * + * Add the name to the hash dict if not present. + * + * Returns the internal copy of the name or NULL in case of internal error + * + * OOM: possible --> returns NULL and OOM flag is set + */ +XMLPUBFUNEXPORT const xmlChar* +xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) { + unsigned long key, okey, nbi = 0; + xmlDictEntryPtr entry; + xmlDictEntryPtr insert; + const xmlChar *ret; + + if (!dict || !name) + return(NULL); + + if (len < 0) + len = xmlStrlen(name); + + /* + * Check for duplicate and insertion location. + */ + okey = xmlDictComputeKey(name, len); + key = okey % dict->size; + if (dict->dict[key].valid == 0) { + insert = NULL; + } else { + for (insert = &(dict->dict[key]); + insert->next; + insert = insert->next) + { + +#ifdef __GNUC__ + if (insert->len == len) { + if (!memcmp(insert->name, name, len)) + return(insert->name); + } +#else + if ((insert->len == len) && (!xmlStrncmp(insert->name, name, len))) + return(insert->name); +#endif + nbi++; + } +#ifdef __GNUC__ + if (insert->len == len) { + if (!memcmp(insert->name, name, len)) + return(insert->name); + } +#else + if ((insert->len == len) && (!xmlStrncmp(insert->name, name, len))) + return(insert->name); +#endif + } + + if (dict->subdict) { + key = okey % dict->subdict->size; + if (dict->subdict->dict[key].valid != 0) { + xmlDictEntryPtr tmp; + + for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL; tmp = tmp->next) + { +#ifdef __GNUC__ + if (tmp->len == len) { + if (!memcmp(tmp->name, name, len)) + return(tmp->name); + } +#else + if ((tmp->len == len) && (!xmlStrncmp(tmp->name, name, len))) + return(tmp->name); +#endif + nbi++; + } +#ifdef __GNUC__ + if (tmp->len == len) { + if (!memcmp(tmp->name, name, len)) + return(tmp->name); + } +#else + if ((tmp->len == len) && (!xmlStrncmp(tmp->name, name, len))) + return(tmp->name); +#endif + } // if (dict->subdict->dict[key].valid != 0) + + key = okey % dict->size; + } // if (dict->subdict) + + ret = xmlDictAddString(dict, name, len); // may set OOM flag + if (!ret) + return(NULL); + + if (!insert) { + entry = &(dict->dict[key]); + } else { + entry = (xmlDictEntryPtr)xmlMalloc(sizeof(xmlDictEntry)); + if (!entry) + return(NULL); //OOM + } + entry->name = ret; + entry->len = len; + entry->next = NULL; + entry->valid = 1; + + if (insert) + insert->next = entry; + + dict->nbElems++; + + if ((nbi > MAX_HASH_LEN) && + (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) + { + xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size); + if(OOM_FLAG) + return NULL; + } + + /* Note that entry may have been freed at this point by xmlDictGrow */ + + return(ret); +} + +/** + * xmlDictQLookup: + * @param dict the dictionnary + * @param prefix the prefix + * @param name the name + * + * Add the QName prefix:name to the hash dict if not present. + * + * Returns the internal copy of the QName or NULL in case of internal error + */ +XMLPUBFUNEXPORT const xmlChar * +xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) { + unsigned long okey, key, nbi = 0; + xmlDictEntryPtr entry; + xmlDictEntryPtr insert; + const xmlChar *ret; + int len; + + if (!dict || !name) + return(NULL); + + len = xmlStrlen(name); + if (prefix) + len += 1 + xmlStrlen(prefix); + + /* + * Check for duplicate and insertion location. + */ + okey = xmlDictComputeQKey(prefix, name, len); + key = okey % dict->size; + if (dict->dict[key].valid == 0) { + insert = NULL; + } else { + for (insert = &(dict->dict[key]); + insert->next; + insert = insert->next) + { + if ((insert->len == len) && xmlStrQEqual(prefix, name, insert->name)) + return(insert->name); + nbi++; + } + if ((insert->len == len) && xmlStrQEqual(prefix, name, insert->name)) + return(insert->name); + } + + if (dict->subdict) + { + key = okey % dict->subdict->size; + if (dict->subdict->dict[key].valid != 0) + { + xmlDictEntryPtr tmp; + for (tmp = &(dict->subdict->dict[key]); + tmp->next; + tmp = tmp->next) + { + if ((tmp->len == len) && xmlStrQEqual(prefix, name, tmp->name)) + return(tmp->name); + nbi++; + } + if ((tmp->len == len) && xmlStrQEqual(prefix, name, tmp->name)) + return(tmp->name); + } + key = okey % dict->size; + } + + ret = xmlDictAddQString(dict, prefix, name, len); + if (!ret) + return(NULL); + if (!insert) { + entry = &(dict->dict[key]); + } else { + entry = (xmlDictEntryPtr)xmlMalloc(sizeof(xmlDictEntry)); + if (!entry) + return(NULL); + } + entry->name = ret; + entry->len = len; + entry->next = NULL; + entry->valid = 1; + + if (insert) + insert->next = entry; + + dict->nbElems++; + + if ((nbi > MAX_HASH_LEN) && + (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) + { + xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size); + } + /* Note that entry may have been freed at this point by xmlDictGrow */ + + return(ret); +} + + +/** + * xmlDictOwns: + * @param dict the dictionnary + * @param str the string + * + * check if a string is owned by the disctionary + * + * Returns 1 if true, 0 if false and -1 in case of error + * -1 in case of error + * + * OOM: impossible + */ +XMLPUBFUNEXPORT int +xmlDictOwns(xmlDictPtr dict, const xmlChar *str) { + xmlDictStringsPtr pool; + + if (!dict || !str) + { + return(-1); + } + + pool = dict->strings; + while (pool) { + if ((str >= pool->array) && (str <= pool->free)) + { + return(1); + } + pool = pool->next; + } + + if (dict->subdict) + return(xmlDictOwns(dict->subdict, str)); + return(0); +} + +#ifndef XMLENGINE_EXCLUDE_UNUSED +/** + * xmlDictSize: + * @param dict the dictionnary + * + * Query the number of elements installed in the hash dict. + * + * Returns the number of elements in the dictionnary or + * -1 in case of error + */ +int +xmlDictSize(xmlDictPtr dict) +{ + if (!dict) + return(-1); + if (dict->subdict) + return(dict->nbElems + dict->subdict->nbElems); + return(dict->nbElems); +} +#endif /* ifndef XMLENGINE_EXCLUDE_UNUSED */