blob: 33fdec8fe13595cfe591987ea51fa72349937b31 [file] [log] [blame]
/*
* hash.c: chained hash tables
*
* Reference: Your favorite introductory book on algorithms
*
* Copyright (C) 2000 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
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
*
* Author: breese@users.sourceforge.net
*/
#include "libxml.h"
#include <string.h>
#include <libxml/hash.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/xmlerror.h>
#include <libxml/globals.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;
};
/*
* The entire hash table
*/
struct _xmlHashTable {
struct _xmlHashEntry **table;
int size;
int nbElems;
};
/*
* xmlHashComputeKey:
* Calculate the hash key
*/
static unsigned long
xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
const xmlChar *name2, const xmlChar *name3) {
unsigned long value = 0L;
char ch;
if (name != NULL) {
value += 30 * (*name);
while ((ch = *name++) != 0) {
/* value *= 31; */
value += (unsigned long)ch;
}
}
if (name2 != NULL) {
while ((ch = *name2++) != 0) {
/* value *= 31; */
value += (unsigned long)ch;
}
}
if (name3 != NULL) {
while ((ch = *name3++) != 0) {
/* value *= 31; */
value += (unsigned long)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 occured.
*/
xmlHashTablePtr
xmlHashCreate(int size) {
xmlHashTablePtr table;
if (size <= 0)
size = 256;
table = xmlMalloc(sizeof(xmlHashTable));
if (table) {
table->size = size;
table->nbElems = 0;
table->table = xmlMalloc(size * sizeof(xmlHashEntryPtr));
if (table->table) {
memset(table->table, 0, size * sizeof(xmlHashEntryPtr));
return(table);
}
xmlFree(table);
}
return(NULL);
}
/**
* 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(xmlHashEntryPtr));
if (table->table == NULL) {
table->table = oldtable;
return(-1);
}
memset(table->table, 0, size * sizeof(xmlHashEntryPtr));
table->size = size;
for (i = 0; i < oldsize; i++) {
iter = oldtable[i];
while (iter) {
next = iter->next;
/*
* put back the entry in the new table
*/
key = xmlHashComputeKey(table, iter->name, iter->name2,
iter->name3);
iter->next = table->table[key];
table->table[key] = 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;
if (table == NULL)
return;
if (table->table) {
for(i = 0; i < table->size; i++) {
iter = table->table[i];
while (iter) {
next = iter->next;
if (f)
f(iter->payload, iter->name);
if (iter->name)
xmlFree(iter->name);
if (iter->name2)
xmlFree(iter->name2);
if (iter->name3)
xmlFree(iter->name3);
iter->payload = NULL;
xmlFree(iter);
iter = next;
}
table->table[i] = NULL;
}
xmlFree(table->table);
}
xmlFree(table);
}
/**
* 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));
}
/**
* 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);
/*
* Check for duplicate and insertion location.
*/
key = xmlHashComputeKey(table, name, name2, name3);
if (table->table[key] == NULL) {
insert = NULL;
} 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);
}
entry = xmlMalloc(sizeof(xmlHashEntry));
if (entry == NULL)
return(-1);
entry->name = xmlStrdup(name);
entry->name2 = xmlStrdup(name2);
entry->name3 = xmlStrdup(name3);
entry->payload = userdata;
entry->next = NULL;
if (insert == NULL) {
table->table[key] = entry;
} else {
insert->next = entry;
}
table->nbElems++;
if (len > MAX_HASH_LEN)
xmlHashGrow(table, MAX_HASH_LEN * table->size);
return(0);
}
/**
* 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);
/*
* Check for duplicate and insertion location.
*/
key = xmlHashComputeKey(table, name, name2, name3);
if (table->table[key] == NULL) {
insert = NULL;
} 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);
}
}
entry = xmlMalloc(sizeof(xmlHashEntry));
if (entry == NULL)
return(-1);
entry->name = xmlStrdup(name);
entry->name2 = xmlStrdup(name2);
entry->name3 = xmlStrdup(name3);
entry->payload = userdata;
entry->next = NULL;
table->nbElems++;
if (insert == NULL) {
table->table[key] = entry;
} else {
insert->next = entry;
}
return(0);
}
/**
* 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);
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);
}
/**
* 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) {
xmlHashScanFull (table, (xmlHashScannerFull) f, data);
}
/**
* 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;
xmlHashEntryPtr iter;
xmlHashEntryPtr next;
if (table == NULL)
return;
if (f == NULL)
return;
if (table->table) {
for(i = 0; i < table->size; i++) {
iter = table->table[i];
while (iter) {
next = iter->next;
if (f)
f(iter->payload, data, iter->name,
iter->name2, iter->name3);
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) {
xmlHashScanFull3 (table, name, name2, name3,
(xmlHashScannerFull) f, data);
}
/**
* 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++) {
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)))) {
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 (table->table) {
for(i = 0; i < table->size; i++) {
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] == NULL) {
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)
f(entry->payload, entry->name);
entry->payload = 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;
else
table->table[key] = entry->next;
xmlFree(entry);
table->nbElems--;
return(0);
}
prev = entry;
}
return(-1);
}
}