mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 08:49:15 +00:00
1191 lines
30 KiB
C
1191 lines
30 KiB
C
/*
|
|
* hash.c: chained hash tables
|
|
*
|
|
* Reference: Your favorite introductory book on algorithms
|
|
*
|
|
* Copyright (C) 2000,2012 Bjorn Reese and 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
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
|
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
|
*
|
|
* Author: breese@users.sourceforge.net
|
|
*/
|
|
|
|
#define IN_LIBXML
|
|
#include "libxml.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
/*
|
|
* Following http://www.ocert.org/advisories/ocert-2011-003.html
|
|
* it seems that having hash randomization might be a good idea
|
|
* when using XML with untrusted data
|
|
*/
|
|
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
|
|
#define HASH_RANDOMIZATION
|
|
#endif
|
|
|
|
#include <libxml/parser.h>
|
|
#include <libxml/hash.h>
|
|
#include <libxml/xmlmemory.h>
|
|
#include <libxml/xmlerror.h>
|
|
#include <libxml/globals.h>
|
|
|
|
#include "private/dict.h"
|
|
|
|
#define MAX_HASH_LEN 8
|
|
|
|
/* #define DEBUG_GROW */
|
|
|
|
/*
|
|
* A single entry in the hash table
|
|
*/
|
|
typedef struct _xmlHashEntry xmlHashEntry;
|
|
typedef xmlHashEntry *xmlHashEntryPtr;
|
|
struct _xmlHashEntry {
|
|
struct _xmlHashEntry *next;
|
|
xmlChar *name;
|
|
xmlChar *name2;
|
|
xmlChar *name3;
|
|
void *payload;
|
|
int valid;
|
|
};
|
|
|
|
/*
|
|
* The entire hash table
|
|
*/
|
|
struct _xmlHashTable {
|
|
struct _xmlHashEntry *table;
|
|
int size;
|
|
int nbElems;
|
|
xmlDictPtr dict;
|
|
#ifdef HASH_RANDOMIZATION
|
|
int random_seed;
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* xmlHashComputeKey:
|
|
* Calculate the hash key
|
|
*/
|
|
#ifdef __clang__
|
|
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
|
|
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
|
|
#endif
|
|
static unsigned long
|
|
xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3) {
|
|
unsigned long value = 0L;
|
|
unsigned long ch;
|
|
|
|
#ifdef HASH_RANDOMIZATION
|
|
value = table->random_seed;
|
|
#endif
|
|
if (name != NULL) {
|
|
value += 30 * (*name);
|
|
while ((ch = *name++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3));
|
|
if (name2 != NULL) {
|
|
while ((ch = *name2++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3));
|
|
if (name3 != NULL) {
|
|
while ((ch = *name3++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
return (value % table->size);
|
|
}
|
|
|
|
#ifdef __clang__
|
|
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
|
|
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
|
|
#endif
|
|
static unsigned long
|
|
xmlHashComputeQKey(xmlHashTablePtr table,
|
|
const xmlChar *prefix, const xmlChar *name,
|
|
const xmlChar *prefix2, const xmlChar *name2,
|
|
const xmlChar *prefix3, const xmlChar *name3) {
|
|
unsigned long value = 0L;
|
|
unsigned long ch;
|
|
|
|
#ifdef HASH_RANDOMIZATION
|
|
value = table->random_seed;
|
|
#endif
|
|
if (prefix != NULL)
|
|
value += 30 * (*prefix);
|
|
else
|
|
value += 30 * (*name);
|
|
|
|
if (prefix != NULL) {
|
|
while ((ch = *prefix++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3) + ':');
|
|
}
|
|
if (name != NULL) {
|
|
while ((ch = *name++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3));
|
|
if (prefix2 != NULL) {
|
|
while ((ch = *prefix2++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3) + ':');
|
|
}
|
|
if (name2 != NULL) {
|
|
while ((ch = *name2++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3));
|
|
if (prefix3 != NULL) {
|
|
while ((ch = *prefix3++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
value = value ^ ((value << 5) + (value >> 3) + ':');
|
|
}
|
|
if (name3 != NULL) {
|
|
while ((ch = *name3++) != 0) {
|
|
value = value ^ ((value << 5) + (value >> 3) + ch);
|
|
}
|
|
}
|
|
return (value % table->size);
|
|
}
|
|
|
|
/**
|
|
* xmlHashCreate:
|
|
* @size: the size of the hash table
|
|
*
|
|
* Create a new xmlHashTablePtr.
|
|
*
|
|
* Returns the newly created object, or NULL if an error occurred.
|
|
*/
|
|
xmlHashTablePtr
|
|
xmlHashCreate(int size) {
|
|
xmlHashTablePtr table;
|
|
|
|
xmlInitParser();
|
|
|
|
if (size <= 0)
|
|
size = 256;
|
|
|
|
table = xmlMalloc(sizeof(xmlHashTable));
|
|
if (table) {
|
|
table->dict = NULL;
|
|
table->size = size;
|
|
table->nbElems = 0;
|
|
table->table = xmlMalloc(size * sizeof(xmlHashEntry));
|
|
if (table->table) {
|
|
memset(table->table, 0, size * sizeof(xmlHashEntry));
|
|
#ifdef HASH_RANDOMIZATION
|
|
table->random_seed = __xmlRandom();
|
|
#endif
|
|
return(table);
|
|
}
|
|
xmlFree(table);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* xmlHashCreateDict:
|
|
* @size: the size of the hash table
|
|
* @dict: a dictionary to use for the hash
|
|
*
|
|
* Create a new xmlHashTablePtr which will use @dict as the internal dictionary
|
|
*
|
|
* Returns the newly created object, or NULL if an error occurred.
|
|
*/
|
|
xmlHashTablePtr
|
|
xmlHashCreateDict(int size, xmlDictPtr dict) {
|
|
xmlHashTablePtr table;
|
|
|
|
table = xmlHashCreate(size);
|
|
if (table != NULL) {
|
|
table->dict = dict;
|
|
xmlDictReference(dict);
|
|
}
|
|
return(table);
|
|
}
|
|
|
|
/**
|
|
* xmlHashGrow:
|
|
* @table: the hash table
|
|
* @size: the new size of the hash table
|
|
*
|
|
* resize the hash table
|
|
*
|
|
* Returns 0 in case of success, -1 in case of failure
|
|
*/
|
|
static int
|
|
xmlHashGrow(xmlHashTablePtr table, int size) {
|
|
unsigned long key;
|
|
int oldsize, i;
|
|
xmlHashEntryPtr iter, next;
|
|
struct _xmlHashEntry *oldtable;
|
|
#ifdef DEBUG_GROW
|
|
unsigned long nbElem = 0;
|
|
#endif
|
|
|
|
if (table == NULL)
|
|
return(-1);
|
|
if (size < 8)
|
|
return(-1);
|
|
if (size > 8 * 2048)
|
|
return(-1);
|
|
|
|
oldsize = table->size;
|
|
oldtable = table->table;
|
|
if (oldtable == NULL)
|
|
return(-1);
|
|
|
|
table->table = xmlMalloc(size * sizeof(xmlHashEntry));
|
|
if (table->table == NULL) {
|
|
table->table = oldtable;
|
|
return(-1);
|
|
}
|
|
memset(table->table, 0, size * sizeof(xmlHashEntry));
|
|
table->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 table. 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 (oldtable[i].valid == 0)
|
|
continue;
|
|
key = xmlHashComputeKey(table, oldtable[i].name, oldtable[i].name2,
|
|
oldtable[i].name3);
|
|
memcpy(&(table->table[key]), &(oldtable[i]), sizeof(xmlHashEntry));
|
|
table->table[key].next = NULL;
|
|
}
|
|
|
|
for (i = 0; i < oldsize; i++) {
|
|
iter = oldtable[i].next;
|
|
while (iter) {
|
|
next = iter->next;
|
|
|
|
/*
|
|
* put back the entry in the new table
|
|
*/
|
|
|
|
key = xmlHashComputeKey(table, iter->name, iter->name2,
|
|
iter->name3);
|
|
if (table->table[key].valid == 0) {
|
|
memcpy(&(table->table[key]), iter, sizeof(xmlHashEntry));
|
|
table->table[key].next = NULL;
|
|
xmlFree(iter);
|
|
} else {
|
|
iter->next = table->table[key].next;
|
|
table->table[key].next = iter;
|
|
}
|
|
|
|
#ifdef DEBUG_GROW
|
|
nbElem++;
|
|
#endif
|
|
|
|
iter = next;
|
|
}
|
|
}
|
|
|
|
xmlFree(oldtable);
|
|
|
|
#ifdef DEBUG_GROW
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
|
|
#endif
|
|
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlHashFree:
|
|
* @table: the hash table
|
|
* @f: the deallocator function for items in the hash
|
|
*
|
|
* Free the hash @table and its contents. The userdata is
|
|
* deallocated with @f if provided.
|
|
*/
|
|
void
|
|
xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
|
|
int i;
|
|
xmlHashEntryPtr iter;
|
|
xmlHashEntryPtr next;
|
|
int inside_table = 0;
|
|
int nbElems;
|
|
|
|
if (table == NULL)
|
|
return;
|
|
if (table->table) {
|
|
nbElems = table->nbElems;
|
|
for(i = 0; (i < table->size) && (nbElems > 0); i++) {
|
|
iter = &(table->table[i]);
|
|
if (iter->valid == 0)
|
|
continue;
|
|
inside_table = 1;
|
|
while (iter) {
|
|
next = iter->next;
|
|
if ((f != NULL) && (iter->payload != NULL))
|
|
f(iter->payload, iter->name);
|
|
if (table->dict == NULL) {
|
|
if (iter->name)
|
|
xmlFree(iter->name);
|
|
if (iter->name2)
|
|
xmlFree(iter->name2);
|
|
if (iter->name3)
|
|
xmlFree(iter->name3);
|
|
}
|
|
iter->payload = NULL;
|
|
if (!inside_table)
|
|
xmlFree(iter);
|
|
nbElems--;
|
|
inside_table = 0;
|
|
iter = next;
|
|
}
|
|
}
|
|
xmlFree(table->table);
|
|
}
|
|
if (table->dict)
|
|
xmlDictFree(table->dict);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* xmlHashDefaultDeallocator:
|
|
* @entry: the hash table entry
|
|
* @name: the entry's name
|
|
*
|
|
* Free a hash table entry with xmlFree.
|
|
*/
|
|
void
|
|
xmlHashDefaultDeallocator(void *entry, const xmlChar *name ATTRIBUTE_UNUSED) {
|
|
xmlFree(entry);
|
|
}
|
|
|
|
/**
|
|
* xmlHashAddEntry:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the @name. Duplicate names generate errors.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
|
|
return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
|
|
}
|
|
|
|
/**
|
|
* xmlHashAddEntry2:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the (@name, @name2) tuple. Duplicate tuples generate errors.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, void *userdata) {
|
|
return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
|
|
}
|
|
|
|
/**
|
|
* xmlHashUpdateEntry:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
* @f: the deallocator function for replaced item (if any)
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the @name. Existing entry for this @name will be removed
|
|
* and freed with @f if found.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
|
|
void *userdata, xmlHashDeallocator f) {
|
|
return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
|
|
}
|
|
|
|
/**
|
|
* xmlHashUpdateEntry2:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
* @f: the deallocator function for replaced item (if any)
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the (@name, @name2) tuple. Existing entry for this tuple will
|
|
* be removed and freed with @f if found.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, void *userdata,
|
|
xmlHashDeallocator f) {
|
|
return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
|
|
}
|
|
|
|
/**
|
|
* xmlHashLookup:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
*
|
|
* Find the userdata specified by the @name.
|
|
*
|
|
* Returns the pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
|
|
return(xmlHashLookup3(table, name, NULL, NULL));
|
|
}
|
|
|
|
/**
|
|
* xmlHashLookup2:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
*
|
|
* Find the userdata specified by the (@name, @name2) tuple.
|
|
*
|
|
* Returns the pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2) {
|
|
return(xmlHashLookup3(table, name, name2, NULL));
|
|
}
|
|
|
|
/**
|
|
* xmlHashQLookup:
|
|
* @table: the hash table
|
|
* @prefix: the prefix of the userdata
|
|
* @name: the name of the userdata
|
|
*
|
|
* Find the userdata specified by the QName @prefix:@name/@name.
|
|
*
|
|
* Returns the pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashQLookup(xmlHashTablePtr table, const xmlChar *prefix,
|
|
const xmlChar *name) {
|
|
return(xmlHashQLookup3(table, prefix, name, NULL, NULL, NULL, NULL));
|
|
}
|
|
|
|
/**
|
|
* xmlHashQLookup2:
|
|
* @table: the hash table
|
|
* @prefix: the prefix of the userdata
|
|
* @name: the name of the userdata
|
|
* @prefix2: the second prefix of the userdata
|
|
* @name2: a second name of the userdata
|
|
*
|
|
* Find the userdata specified by the QNames tuple
|
|
*
|
|
* Returns the pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashQLookup2(xmlHashTablePtr table, const xmlChar *prefix,
|
|
const xmlChar *name, const xmlChar *prefix2,
|
|
const xmlChar *name2) {
|
|
return(xmlHashQLookup3(table, prefix, name, prefix2, name2, NULL, NULL));
|
|
}
|
|
|
|
/**
|
|
* xmlHashAddEntry3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @name3: a third name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the tuple (@name, @name2, @name3). Duplicate entries generate
|
|
* errors.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3,
|
|
void *userdata) {
|
|
unsigned long key, len = 0;
|
|
xmlHashEntryPtr entry;
|
|
xmlHashEntryPtr insert;
|
|
|
|
if ((table == NULL) || (name == NULL))
|
|
return(-1);
|
|
|
|
/*
|
|
* If using a dict internalize if needed
|
|
*/
|
|
if (table->dict) {
|
|
if (!xmlDictOwns(table->dict, name)) {
|
|
name = xmlDictLookup(table->dict, name, -1);
|
|
if (name == NULL)
|
|
return(-1);
|
|
}
|
|
if ((name2 != NULL) && (!xmlDictOwns(table->dict, name2))) {
|
|
name2 = xmlDictLookup(table->dict, name2, -1);
|
|
if (name2 == NULL)
|
|
return(-1);
|
|
}
|
|
if ((name3 != NULL) && (!xmlDictOwns(table->dict, name3))) {
|
|
name3 = xmlDictLookup(table->dict, name3, -1);
|
|
if (name3 == NULL)
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for duplicate and insertion location.
|
|
*/
|
|
key = xmlHashComputeKey(table, name, name2, name3);
|
|
if (table->table[key].valid == 0) {
|
|
insert = NULL;
|
|
} else {
|
|
if (table->dict) {
|
|
for (insert = &(table->table[key]); insert->next != NULL;
|
|
insert = insert->next) {
|
|
if ((insert->name == name) &&
|
|
(insert->name2 == name2) &&
|
|
(insert->name3 == name3))
|
|
return(-1);
|
|
len++;
|
|
}
|
|
if ((insert->name == name) &&
|
|
(insert->name2 == name2) &&
|
|
(insert->name3 == name3))
|
|
return(-1);
|
|
} else {
|
|
for (insert = &(table->table[key]); insert->next != NULL;
|
|
insert = insert->next) {
|
|
if ((xmlStrEqual(insert->name, name)) &&
|
|
(xmlStrEqual(insert->name2, name2)) &&
|
|
(xmlStrEqual(insert->name3, name3)))
|
|
return(-1);
|
|
len++;
|
|
}
|
|
if ((xmlStrEqual(insert->name, name)) &&
|
|
(xmlStrEqual(insert->name2, name2)) &&
|
|
(xmlStrEqual(insert->name3, name3)))
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
if (insert == NULL) {
|
|
entry = &(table->table[key]);
|
|
} else {
|
|
entry = xmlMalloc(sizeof(xmlHashEntry));
|
|
if (entry == NULL)
|
|
return(-1);
|
|
}
|
|
|
|
if (table->dict != NULL) {
|
|
entry->name = (xmlChar *) name;
|
|
entry->name2 = (xmlChar *) name2;
|
|
entry->name3 = (xmlChar *) name3;
|
|
} else {
|
|
entry->name = xmlStrdup(name);
|
|
if (entry->name == NULL) {
|
|
entry->name2 = NULL;
|
|
goto error;
|
|
}
|
|
if (name2 == NULL) {
|
|
entry->name2 = NULL;
|
|
} else {
|
|
entry->name2 = xmlStrdup(name2);
|
|
if (entry->name2 == NULL)
|
|
goto error;
|
|
}
|
|
if (name3 == NULL) {
|
|
entry->name3 = NULL;
|
|
} else {
|
|
entry->name3 = xmlStrdup(name3);
|
|
if (entry->name3 == NULL)
|
|
goto error;
|
|
}
|
|
}
|
|
entry->payload = userdata;
|
|
entry->next = NULL;
|
|
entry->valid = 1;
|
|
|
|
|
|
if (insert != NULL)
|
|
insert->next = entry;
|
|
|
|
table->nbElems++;
|
|
|
|
if (len > MAX_HASH_LEN)
|
|
xmlHashGrow(table, MAX_HASH_LEN * table->size);
|
|
|
|
return(0);
|
|
|
|
error:
|
|
xmlFree(entry->name2);
|
|
xmlFree(entry->name);
|
|
if (insert != NULL)
|
|
xmlFree(entry);
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* xmlHashUpdateEntry3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @name3: a third name of the userdata
|
|
* @userdata: a pointer to the userdata
|
|
* @f: the deallocator function for replaced item (if any)
|
|
*
|
|
* Add the @userdata to the hash @table. This can later be retrieved
|
|
* by using the tuple (@name, @name2, @name3). Existing entry for this tuple
|
|
* will be removed and freed with @f if found.
|
|
*
|
|
* Returns 0 the addition succeeded and -1 in case of error.
|
|
*/
|
|
int
|
|
xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3,
|
|
void *userdata, xmlHashDeallocator f) {
|
|
unsigned long key;
|
|
xmlHashEntryPtr entry;
|
|
xmlHashEntryPtr insert;
|
|
|
|
if ((table == NULL) || name == NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* If using a dict internalize if needed
|
|
*/
|
|
if (table->dict) {
|
|
if (!xmlDictOwns(table->dict, name)) {
|
|
name = xmlDictLookup(table->dict, name, -1);
|
|
if (name == NULL)
|
|
return(-1);
|
|
}
|
|
if ((name2 != NULL) && (!xmlDictOwns(table->dict, name2))) {
|
|
name2 = xmlDictLookup(table->dict, name2, -1);
|
|
if (name2 == NULL)
|
|
return(-1);
|
|
}
|
|
if ((name3 != NULL) && (!xmlDictOwns(table->dict, name3))) {
|
|
name3 = xmlDictLookup(table->dict, name3, -1);
|
|
if (name3 == NULL)
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for duplicate and insertion location.
|
|
*/
|
|
key = xmlHashComputeKey(table, name, name2, name3);
|
|
if (table->table[key].valid == 0) {
|
|
insert = NULL;
|
|
} else {
|
|
if (table ->dict) {
|
|
for (insert = &(table->table[key]); insert->next != NULL;
|
|
insert = insert->next) {
|
|
if ((insert->name == name) &&
|
|
(insert->name2 == name2) &&
|
|
(insert->name3 == name3)) {
|
|
if (f)
|
|
f(insert->payload, insert->name);
|
|
insert->payload = userdata;
|
|
return(0);
|
|
}
|
|
}
|
|
if ((insert->name == name) &&
|
|
(insert->name2 == name2) &&
|
|
(insert->name3 == name3)) {
|
|
if (f)
|
|
f(insert->payload, insert->name);
|
|
insert->payload = userdata;
|
|
return(0);
|
|
}
|
|
} else {
|
|
for (insert = &(table->table[key]); insert->next != NULL;
|
|
insert = insert->next) {
|
|
if ((xmlStrEqual(insert->name, name)) &&
|
|
(xmlStrEqual(insert->name2, name2)) &&
|
|
(xmlStrEqual(insert->name3, name3))) {
|
|
if (f)
|
|
f(insert->payload, insert->name);
|
|
insert->payload = userdata;
|
|
return(0);
|
|
}
|
|
}
|
|
if ((xmlStrEqual(insert->name, name)) &&
|
|
(xmlStrEqual(insert->name2, name2)) &&
|
|
(xmlStrEqual(insert->name3, name3))) {
|
|
if (f)
|
|
f(insert->payload, insert->name);
|
|
insert->payload = userdata;
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (insert == NULL) {
|
|
entry = &(table->table[key]);
|
|
} else {
|
|
entry = xmlMalloc(sizeof(xmlHashEntry));
|
|
if (entry == NULL)
|
|
return(-1);
|
|
}
|
|
|
|
if (table->dict != NULL) {
|
|
entry->name = (xmlChar *) name;
|
|
entry->name2 = (xmlChar *) name2;
|
|
entry->name3 = (xmlChar *) name3;
|
|
} else {
|
|
entry->name = xmlStrdup(name);
|
|
if (entry->name == NULL) {
|
|
entry->name2 = NULL;
|
|
goto error;
|
|
}
|
|
if (name2 == NULL) {
|
|
entry->name2 = NULL;
|
|
} else {
|
|
entry->name2 = xmlStrdup(name2);
|
|
if (entry->name2 == NULL)
|
|
goto error;
|
|
}
|
|
if (name3 == NULL) {
|
|
entry->name3 = NULL;
|
|
} else {
|
|
entry->name3 = xmlStrdup(name3);
|
|
if (entry->name3 == NULL)
|
|
goto error;
|
|
}
|
|
}
|
|
entry->payload = userdata;
|
|
entry->next = NULL;
|
|
entry->valid = 1;
|
|
table->nbElems++;
|
|
|
|
|
|
if (insert != NULL) {
|
|
insert->next = entry;
|
|
}
|
|
return(0);
|
|
|
|
error:
|
|
xmlFree(entry->name2);
|
|
xmlFree(entry->name);
|
|
if (insert != NULL)
|
|
xmlFree(entry);
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* xmlHashLookup3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @name3: a third name of the userdata
|
|
*
|
|
* Find the userdata specified by the (@name, @name2, @name3) tuple.
|
|
*
|
|
* Returns the a pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3) {
|
|
unsigned long key;
|
|
xmlHashEntryPtr entry;
|
|
|
|
if (table == NULL)
|
|
return(NULL);
|
|
if (name == NULL)
|
|
return(NULL);
|
|
key = xmlHashComputeKey(table, name, name2, name3);
|
|
if (table->table[key].valid == 0)
|
|
return(NULL);
|
|
if (table->dict) {
|
|
for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
|
|
if ((entry->name == name) &&
|
|
(entry->name2 == name2) &&
|
|
(entry->name3 == name3))
|
|
return(entry->payload);
|
|
}
|
|
}
|
|
for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
|
|
if ((xmlStrEqual(entry->name, name)) &&
|
|
(xmlStrEqual(entry->name2, name2)) &&
|
|
(xmlStrEqual(entry->name3, name3)))
|
|
return(entry->payload);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* xmlHashQLookup3:
|
|
* @table: the hash table
|
|
* @prefix: the prefix of the userdata
|
|
* @name: the name of the userdata
|
|
* @prefix2: the second prefix of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @prefix3: the third prefix of the userdata
|
|
* @name3: a third name of the userdata
|
|
*
|
|
* Find the userdata specified by the (@name, @name2, @name3) tuple.
|
|
*
|
|
* Returns the a pointer to the userdata
|
|
*/
|
|
void *
|
|
xmlHashQLookup3(xmlHashTablePtr table,
|
|
const xmlChar *prefix, const xmlChar *name,
|
|
const xmlChar *prefix2, const xmlChar *name2,
|
|
const xmlChar *prefix3, const xmlChar *name3) {
|
|
unsigned long key;
|
|
xmlHashEntryPtr entry;
|
|
|
|
if (table == NULL)
|
|
return(NULL);
|
|
if (name == NULL)
|
|
return(NULL);
|
|
key = xmlHashComputeQKey(table, prefix, name, prefix2,
|
|
name2, prefix3, name3);
|
|
if (table->table[key].valid == 0)
|
|
return(NULL);
|
|
for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
|
|
if ((xmlStrQEqual(prefix, name, entry->name)) &&
|
|
(xmlStrQEqual(prefix2, name2, entry->name2)) &&
|
|
(xmlStrQEqual(prefix3, name3, entry->name3)))
|
|
return(entry->payload);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
typedef struct {
|
|
xmlHashScanner hashscanner;
|
|
void *data;
|
|
} stubData;
|
|
|
|
static void
|
|
stubHashScannerFull (void *payload, void *data, const xmlChar *name,
|
|
const xmlChar *name2 ATTRIBUTE_UNUSED,
|
|
const xmlChar *name3 ATTRIBUTE_UNUSED) {
|
|
stubData *stubdata = (stubData *) data;
|
|
stubdata->hashscanner (payload, stubdata->data, (xmlChar *) name);
|
|
}
|
|
|
|
/**
|
|
* xmlHashScan:
|
|
* @table: the hash table
|
|
* @f: the scanner function for items in the hash
|
|
* @data: extra data passed to f
|
|
*
|
|
* Scan the hash @table and applied @f to each value.
|
|
*/
|
|
void
|
|
xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
|
|
stubData stubdata;
|
|
stubdata.data = data;
|
|
stubdata.hashscanner = f;
|
|
xmlHashScanFull (table, stubHashScannerFull, &stubdata);
|
|
}
|
|
|
|
/**
|
|
* xmlHashScanFull:
|
|
* @table: the hash table
|
|
* @f: the scanner function for items in the hash
|
|
* @data: extra data passed to f
|
|
*
|
|
* Scan the hash @table and applied @f to each value.
|
|
*/
|
|
void
|
|
xmlHashScanFull(xmlHashTablePtr table, xmlHashScannerFull f, void *data) {
|
|
int i, nb;
|
|
xmlHashEntryPtr iter;
|
|
xmlHashEntryPtr next;
|
|
|
|
if (table == NULL)
|
|
return;
|
|
if (f == NULL)
|
|
return;
|
|
|
|
if (table->table) {
|
|
for(i = 0; i < table->size; i++) {
|
|
if (table->table[i].valid == 0)
|
|
continue;
|
|
iter = &(table->table[i]);
|
|
while (iter) {
|
|
next = iter->next;
|
|
nb = table->nbElems;
|
|
if ((f != NULL) && (iter->payload != NULL))
|
|
f(iter->payload, data, iter->name,
|
|
iter->name2, iter->name3);
|
|
if (nb != table->nbElems) {
|
|
/* table was modified by the callback, be careful */
|
|
if (iter == &(table->table[i])) {
|
|
if (table->table[i].valid == 0)
|
|
iter = NULL;
|
|
if (table->table[i].next != next)
|
|
iter = &(table->table[i]);
|
|
} else
|
|
iter = next;
|
|
} else
|
|
iter = next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlHashScan3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata or NULL
|
|
* @name2: a second name of the userdata or NULL
|
|
* @name3: a third name of the userdata or NULL
|
|
* @f: the scanner function for items in the hash
|
|
* @data: extra data passed to f
|
|
*
|
|
* Scan the hash @table and applied @f to each value matching
|
|
* (@name, @name2, @name3) tuple. If one of the names is null,
|
|
* the comparison is considered to match.
|
|
*/
|
|
void
|
|
xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3,
|
|
xmlHashScanner f, void *data) {
|
|
stubData stubdata;
|
|
stubdata.data = data;
|
|
stubdata.hashscanner = f;
|
|
xmlHashScanFull3(table, name, name2, name3, stubHashScannerFull,
|
|
&stubdata);
|
|
}
|
|
|
|
/**
|
|
* xmlHashScanFull3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata or NULL
|
|
* @name2: a second name of the userdata or NULL
|
|
* @name3: a third name of the userdata or NULL
|
|
* @f: the scanner function for items in the hash
|
|
* @data: extra data passed to f
|
|
*
|
|
* Scan the hash @table and applied @f to each value matching
|
|
* (@name, @name2, @name3) tuple. If one of the names is null,
|
|
* the comparison is considered to match.
|
|
*/
|
|
void
|
|
xmlHashScanFull3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3,
|
|
xmlHashScannerFull f, void *data) {
|
|
int i;
|
|
xmlHashEntryPtr iter;
|
|
xmlHashEntryPtr next;
|
|
|
|
if (table == NULL)
|
|
return;
|
|
if (f == NULL)
|
|
return;
|
|
|
|
if (table->table) {
|
|
for(i = 0; i < table->size; i++) {
|
|
if (table->table[i].valid == 0)
|
|
continue;
|
|
iter = &(table->table[i]);
|
|
while (iter) {
|
|
next = iter->next;
|
|
if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
|
|
((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
|
|
((name3 == NULL) || (xmlStrEqual(name3, iter->name3))) &&
|
|
(iter->payload != NULL)) {
|
|
f(iter->payload, data, iter->name,
|
|
iter->name2, iter->name3);
|
|
}
|
|
iter = next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlHashCopy:
|
|
* @table: the hash table
|
|
* @f: the copier function for items in the hash
|
|
*
|
|
* Scan the hash @table and applied @f to each value.
|
|
*
|
|
* Returns the new table or NULL in case of error.
|
|
*/
|
|
xmlHashTablePtr
|
|
xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
|
|
int i;
|
|
xmlHashEntryPtr iter;
|
|
xmlHashEntryPtr next;
|
|
xmlHashTablePtr ret;
|
|
|
|
if (table == NULL)
|
|
return(NULL);
|
|
if (f == NULL)
|
|
return(NULL);
|
|
|
|
ret = xmlHashCreate(table->size);
|
|
if (ret == NULL)
|
|
return(NULL);
|
|
|
|
if (table->table) {
|
|
for(i = 0; i < table->size; i++) {
|
|
if (table->table[i].valid == 0)
|
|
continue;
|
|
iter = &(table->table[i]);
|
|
while (iter) {
|
|
next = iter->next;
|
|
xmlHashAddEntry3(ret, iter->name, iter->name2,
|
|
iter->name3, f(iter->payload, iter->name));
|
|
iter = next;
|
|
}
|
|
}
|
|
}
|
|
ret->nbElems = table->nbElems;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlHashSize:
|
|
* @table: the hash table
|
|
*
|
|
* Query the number of elements installed in the hash @table.
|
|
*
|
|
* Returns the number of elements in the hash table or
|
|
* -1 in case of error
|
|
*/
|
|
int
|
|
xmlHashSize(xmlHashTablePtr table) {
|
|
if (table == NULL)
|
|
return(-1);
|
|
return(table->nbElems);
|
|
}
|
|
|
|
/**
|
|
* xmlHashRemoveEntry:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @f: the deallocator function for removed item (if any)
|
|
*
|
|
* Find the userdata specified by the @name and remove
|
|
* it from the hash @table. Existing userdata for this tuple will be removed
|
|
* and freed with @f.
|
|
*
|
|
* Returns 0 if the removal succeeded and -1 in case of error or not found.
|
|
*/
|
|
int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
|
|
xmlHashDeallocator f) {
|
|
return(xmlHashRemoveEntry3(table, name, NULL, NULL, f));
|
|
}
|
|
|
|
/**
|
|
* xmlHashRemoveEntry2:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @f: the deallocator function for removed item (if any)
|
|
*
|
|
* Find the userdata specified by the (@name, @name2) tuple and remove
|
|
* it from the hash @table. Existing userdata for this tuple will be removed
|
|
* and freed with @f.
|
|
*
|
|
* Returns 0 if the removal succeeded and -1 in case of error or not found.
|
|
*/
|
|
int
|
|
xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, xmlHashDeallocator f) {
|
|
return(xmlHashRemoveEntry3(table, name, name2, NULL, f));
|
|
}
|
|
|
|
/**
|
|
* xmlHashRemoveEntry3:
|
|
* @table: the hash table
|
|
* @name: the name of the userdata
|
|
* @name2: a second name of the userdata
|
|
* @name3: a third name of the userdata
|
|
* @f: the deallocator function for removed item (if any)
|
|
*
|
|
* Find the userdata specified by the (@name, @name2, @name3) tuple and remove
|
|
* it from the hash @table. Existing userdata for this tuple will be removed
|
|
* and freed with @f.
|
|
*
|
|
* Returns 0 if the removal succeeded and -1 in case of error or not found.
|
|
*/
|
|
int
|
|
xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
|
|
const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
|
|
unsigned long key;
|
|
xmlHashEntryPtr entry;
|
|
xmlHashEntryPtr prev = NULL;
|
|
|
|
if (table == NULL || name == NULL)
|
|
return(-1);
|
|
|
|
key = xmlHashComputeKey(table, name, name2, name3);
|
|
if (table->table[key].valid == 0) {
|
|
return(-1);
|
|
} else {
|
|
for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
|
|
if (xmlStrEqual(entry->name, name) &&
|
|
xmlStrEqual(entry->name2, name2) &&
|
|
xmlStrEqual(entry->name3, name3)) {
|
|
if ((f != NULL) && (entry->payload != NULL))
|
|
f(entry->payload, entry->name);
|
|
entry->payload = NULL;
|
|
if (table->dict == NULL) {
|
|
if(entry->name)
|
|
xmlFree(entry->name);
|
|
if(entry->name2)
|
|
xmlFree(entry->name2);
|
|
if(entry->name3)
|
|
xmlFree(entry->name3);
|
|
}
|
|
if(prev) {
|
|
prev->next = entry->next;
|
|
xmlFree(entry);
|
|
} else {
|
|
if (entry->next == NULL) {
|
|
entry->valid = 0;
|
|
} else {
|
|
entry = entry->next;
|
|
memcpy(&(table->table[key]), entry, sizeof(xmlHashEntry));
|
|
xmlFree(entry);
|
|
}
|
|
}
|
|
table->nbElems--;
|
|
return(0);
|
|
}
|
|
prev = entry;
|
|
}
|
|
return(-1);
|
|
}
|
|
}
|
|
|