xml/libxml2libs/src/libxml2/libxml2_dict.c
changeset 0 e35f40988205
--- /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 <string.h>
+#include <stdapis/libxml2/libxml2_globals.h>
+
+#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 */