| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #define IN_LIBXML |
| #include "libxml.h" |
|
|
| #include <string.h> |
| #include <limits.h> |
|
|
| #include <libxml/parser.h> |
| #include <libxml/hash.h> |
| #include <libxml/dict.h> |
| #include <libxml/xmlmemory.h> |
| #include <libxml/xmlstring.h> |
|
|
| #include "private/dict.h" |
|
|
| #ifndef SIZE_MAX |
| #define SIZE_MAX ((size_t) -1) |
| #endif |
|
|
| #define MAX_FILL_NUM 7 |
| #define MAX_FILL_DENOM 8 |
| #define MIN_HASH_SIZE 8 |
| #define MAX_HASH_SIZE (1u << 31) |
|
|
| |
| |
| |
| typedef struct { |
| unsigned hashValue; |
| |
| xmlChar *key; |
| xmlChar *key2; |
| xmlChar *key3; |
| void *payload; |
| } xmlHashEntry; |
|
|
| |
| |
| |
| struct _xmlHashTable { |
| xmlHashEntry *table; |
| unsigned size; |
| unsigned nbElems; |
| xmlDictPtr dict; |
| unsigned randomSeed; |
| }; |
|
|
| static int |
| xmlHashGrow(xmlHashTablePtr hash, unsigned size); |
|
|
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| static unsigned |
| xmlHashValue(unsigned seed, const xmlChar *key, const xmlChar *key2, |
| const xmlChar *key3, size_t *lengths) { |
| unsigned h1, h2; |
| size_t i; |
|
|
| HASH_INIT(h1, h2, seed); |
|
|
| for (i = 0; key[i] != 0; i++) { |
| HASH_UPDATE(h1, h2, key[i]); |
| } |
| if (lengths) |
| lengths[0] = i; |
|
|
| HASH_UPDATE(h1, h2, 0); |
|
|
| if (key2 != NULL) { |
| for (i = 0; key2[i] != 0; i++) { |
| HASH_UPDATE(h1, h2, key2[i]); |
| } |
| if (lengths) |
| lengths[1] = i; |
| } |
|
|
| HASH_UPDATE(h1, h2, 0); |
|
|
| if (key3 != NULL) { |
| for (i = 0; key3[i] != 0; i++) { |
| HASH_UPDATE(h1, h2, key3[i]); |
| } |
| if (lengths) |
| lengths[2] = i; |
| } |
|
|
| HASH_FINISH(h1, h2); |
|
|
| return(h2); |
| } |
|
|
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| static unsigned |
| xmlHashQNameValue(unsigned seed, |
| const xmlChar *prefix, const xmlChar *name, |
| const xmlChar *prefix2, const xmlChar *name2, |
| const xmlChar *prefix3, const xmlChar *name3) { |
| unsigned h1, h2, ch; |
|
|
| HASH_INIT(h1, h2, seed); |
|
|
| if (prefix != NULL) { |
| while ((ch = *prefix++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| HASH_UPDATE(h1, h2, ':'); |
| } |
| if (name != NULL) { |
| while ((ch = *name++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| } |
| HASH_UPDATE(h1, h2, 0); |
| if (prefix2 != NULL) { |
| while ((ch = *prefix2++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| HASH_UPDATE(h1, h2, ':'); |
| } |
| if (name2 != NULL) { |
| while ((ch = *name2++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| } |
| HASH_UPDATE(h1, h2, 0); |
| if (prefix3 != NULL) { |
| while ((ch = *prefix3++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| HASH_UPDATE(h1, h2, ':'); |
| } |
| if (name3 != NULL) { |
| while ((ch = *name3++) != 0) { |
| HASH_UPDATE(h1, h2, ch); |
| } |
| } |
|
|
| HASH_FINISH(h1, h2); |
|
|
| return(h2); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| xmlHashTable * |
| xmlHashCreate(int size) { |
| xmlHashTablePtr hash; |
|
|
| xmlInitParser(); |
|
|
| hash = xmlMalloc(sizeof(*hash)); |
| if (hash == NULL) |
| return(NULL); |
| hash->dict = NULL; |
| hash->size = 0; |
| hash->table = NULL; |
| hash->nbElems = 0; |
| hash->randomSeed = xmlRandom(); |
| #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
| hash->randomSeed = 0; |
| #endif |
|
|
| |
| |
| |
| |
| |
| if (size > MIN_HASH_SIZE) { |
| unsigned newSize = MIN_HASH_SIZE * 2; |
|
|
| while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE)) |
| newSize *= 2; |
|
|
| if (xmlHashGrow(hash, newSize) != 0) { |
| xmlFree(hash); |
| return(NULL); |
| } |
| } |
|
|
| return(hash); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| xmlHashTable * |
| xmlHashCreateDict(int size, xmlDict *dict) { |
| xmlHashTablePtr hash; |
|
|
| hash = xmlHashCreate(size); |
| if (hash != NULL) { |
| hash->dict = dict; |
| xmlDictReference(dict); |
| } |
| return(hash); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| void |
| xmlHashFree(xmlHashTable *hash, xmlHashDeallocator dealloc) { |
| if (hash == NULL) |
| return; |
|
|
| if (hash->table) { |
| const xmlHashEntry *end = &hash->table[hash->size]; |
| const xmlHashEntry *entry; |
|
|
| for (entry = hash->table; entry < end; entry++) { |
| if (entry->hashValue == 0) |
| continue; |
| if ((dealloc != NULL) && (entry->payload != NULL)) |
| dealloc(entry->payload, entry->key); |
| if (hash->dict == NULL) { |
| if (entry->key) |
| xmlFree(entry->key); |
| if (entry->key2) |
| xmlFree(entry->key2); |
| if (entry->key3) |
| xmlFree(entry->key3); |
| } |
| } |
|
|
| xmlFree(hash->table); |
| } |
|
|
| if (hash->dict) |
| xmlDictFree(hash->dict); |
|
|
| xmlFree(hash); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static int |
| xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) { |
| if (s1 == NULL) |
| return(s2 == NULL); |
| else |
| return((s2 != NULL) && |
| (strcmp((const char *) s1, (const char *) s2) == 0)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| static xmlHashEntry * |
| xmlHashFindEntry(const xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| unsigned hashValue, int *pfound) { |
| xmlHashEntry *entry; |
| unsigned mask, pos, displ; |
| int found = 0; |
|
|
| mask = hash->size - 1; |
| pos = hashValue & mask; |
| entry = &hash->table[pos]; |
|
|
| if (entry->hashValue != 0) { |
| |
| |
| |
| |
| |
| displ = 0; |
| hashValue |= MAX_HASH_SIZE; |
|
|
| do { |
| if (entry->hashValue == hashValue) { |
| if (hash->dict) { |
| if ((entry->key == key) && |
| (entry->key2 == key2) && |
| (entry->key3 == key3)) { |
| found = 1; |
| break; |
| } |
| } |
| if ((strcmp((const char *) entry->key, |
| (const char *) key) == 0) && |
| (xmlFastStrEqual(entry->key2, key2)) && |
| (xmlFastStrEqual(entry->key3, key3))) { |
| found = 1; |
| break; |
| } |
| } |
|
|
| displ++; |
| pos++; |
| entry++; |
| if ((pos & mask) == 0) |
| entry = hash->table; |
| } while ((entry->hashValue != 0) && |
| (((pos - entry->hashValue) & mask) >= displ)); |
| } |
|
|
| *pfound = found; |
| return(entry); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| static int |
| xmlHashGrow(xmlHashTablePtr hash, unsigned size) { |
| const xmlHashEntry *oldentry, *oldend, *end; |
| xmlHashEntry *table; |
| unsigned oldsize, i; |
|
|
| |
| if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0])) |
| return(-1); |
| table = xmlMalloc(size * sizeof(table[0])); |
| if (table == NULL) |
| return(-1); |
| memset(table, 0, size * sizeof(table[0])); |
|
|
| oldsize = hash->size; |
| if (oldsize == 0) |
| goto done; |
|
|
| oldend = &hash->table[oldsize]; |
| end = &table[size]; |
|
|
| |
| |
| |
| |
| |
| |
| |
| oldentry = hash->table; |
| while (oldentry->hashValue != 0) { |
| if (++oldentry >= oldend) |
| oldentry = hash->table; |
| } |
|
|
| for (i = 0; i < oldsize; i++) { |
| if (oldentry->hashValue != 0) { |
| xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)]; |
|
|
| while (entry->hashValue != 0) { |
| if (++entry >= end) |
| entry = table; |
| } |
| *entry = *oldentry; |
| } |
|
|
| if (++oldentry >= oldend) |
| oldentry = hash->table; |
| } |
|
|
| xmlFree(hash->table); |
|
|
| done: |
| hash->table = table; |
| hash->size = size; |
|
|
| return(0); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| static int |
| xmlHashUpdateInternal(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| void *payload, xmlHashDeallocator dealloc, int update) { |
| xmlChar *copy, *copy2, *copy3; |
| xmlHashEntry *entry = NULL; |
| size_t lengths[3] = {0, 0, 0}; |
| unsigned hashValue, newSize; |
|
|
| if ((hash == NULL) || (key == NULL)) |
| return(-1); |
|
|
| hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths); |
|
|
| |
| |
| |
| if (hash->size == 0) { |
| newSize = MIN_HASH_SIZE; |
| } else { |
| int found = 0; |
|
|
| entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); |
|
|
| if (found) { |
| if (update) { |
| if (dealloc) |
| dealloc(entry->payload, entry->key); |
| entry->payload = payload; |
| } |
|
|
| return(0); |
| } |
|
|
| if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) { |
| |
| if (hash->size >= MAX_HASH_SIZE) |
| return(-1); |
| newSize = hash->size * 2; |
| } else { |
| newSize = 0; |
| } |
| } |
|
|
| |
| |
| |
| if (newSize > 0) { |
| unsigned mask, displ, pos; |
|
|
| if (xmlHashGrow(hash, newSize) != 0) |
| return(-1); |
|
|
| |
| |
| |
| mask = hash->size - 1; |
| displ = 0; |
| pos = hashValue & mask; |
| entry = &hash->table[pos]; |
|
|
| if (entry->hashValue != 0) { |
| do { |
| displ++; |
| pos++; |
| entry++; |
| if ((pos & mask) == 0) |
| entry = hash->table; |
| } while ((entry->hashValue != 0) && |
| ((pos - entry->hashValue) & mask) >= displ); |
| } |
| } |
|
|
| |
| |
| |
| if (hash->dict != NULL) { |
| if (xmlDictOwns(hash->dict, key)) { |
| copy = (xmlChar *) key; |
| } else { |
| copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1); |
| if (copy == NULL) |
| return(-1); |
| } |
|
|
| if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) { |
| copy2 = (xmlChar *) key2; |
| } else { |
| copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1); |
| if (copy2 == NULL) |
| return(-1); |
| } |
| if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) { |
| copy3 = (xmlChar *) key3; |
| } else { |
| copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1); |
| if (copy3 == NULL) |
| return(-1); |
| } |
| } else { |
| copy = xmlMalloc(lengths[0] + 1); |
| if (copy == NULL) |
| return(-1); |
| memcpy(copy, key, lengths[0] + 1); |
|
|
| if (key2 != NULL) { |
| copy2 = xmlMalloc(lengths[1] + 1); |
| if (copy2 == NULL) { |
| xmlFree(copy); |
| return(-1); |
| } |
| memcpy(copy2, key2, lengths[1] + 1); |
| } else { |
| copy2 = NULL; |
| } |
|
|
| if (key3 != NULL) { |
| copy3 = xmlMalloc(lengths[2] + 1); |
| if (copy3 == NULL) { |
| xmlFree(copy); |
| xmlFree(copy2); |
| return(-1); |
| } |
| memcpy(copy3, key3, lengths[2] + 1); |
| } else { |
| copy3 = NULL; |
| } |
| } |
|
|
| |
| |
| |
| if (entry->hashValue != 0) { |
| const xmlHashEntry *end = &hash->table[hash->size]; |
| const xmlHashEntry *cur = entry; |
|
|
| do { |
| cur++; |
| if (cur >= end) |
| cur = hash->table; |
| } while (cur->hashValue != 0); |
|
|
| if (cur < entry) { |
| |
| |
| |
| |
| memmove(&hash->table[1], hash->table, |
| (char *) cur - (char *) hash->table); |
| cur = end - 1; |
| hash->table[0] = *cur; |
| } |
|
|
| memmove(&entry[1], entry, (char *) cur - (char *) entry); |
| } |
|
|
| |
| |
| |
| entry->key = copy; |
| entry->key2 = copy2; |
| entry->key3 = copy3; |
| entry->payload = payload; |
| |
| entry->hashValue = hashValue | MAX_HASH_SIZE; |
|
|
| hash->nbElems++; |
|
|
| return(1); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| void |
| xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) { |
| xmlFree(entry); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAdd(xmlHashTable *hash, const xmlChar *key, void *payload) { |
| return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAdd2(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, void *payload) { |
| return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAdd3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| void *payload) { |
| return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAddEntry(xmlHashTable *hash, const xmlChar *key, void *payload) { |
| int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0); |
|
|
| if (res == 0) |
| res = -1; |
| else if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAddEntry2(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, void *payload) { |
| int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0); |
|
|
| if (res == 0) |
| res = -1; |
| else if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashAddEntry3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| void *payload) { |
| int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0); |
|
|
| if (res == 0) |
| res = -1; |
| else if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashUpdateEntry(xmlHashTable *hash, const xmlChar *key, |
| void *payload, xmlHashDeallocator dealloc) { |
| int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, |
| dealloc, 1); |
|
|
| if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashUpdateEntry2(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, void *payload, |
| xmlHashDeallocator dealloc) { |
| int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, |
| dealloc, 1); |
|
|
| if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashUpdateEntry3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| void *payload, xmlHashDeallocator dealloc) { |
| int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, |
| dealloc, 1); |
|
|
| if (res == 1) |
| res = 0; |
|
|
| return(res); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| void * |
| xmlHashLookup(xmlHashTable *hash, const xmlChar *key) { |
| return(xmlHashLookup3(hash, key, NULL, NULL)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| void * |
| xmlHashLookup2(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2) { |
| return(xmlHashLookup3(hash, key, key2, NULL)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| void * |
| xmlHashQLookup(xmlHashTable *hash, const xmlChar *prefix, |
| const xmlChar *name) { |
| return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void * |
| xmlHashQLookup2(xmlHashTable *hash, const xmlChar *prefix, |
| const xmlChar *name, const xmlChar *prefix2, |
| const xmlChar *name2) { |
| return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void * |
| xmlHashLookup3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3) { |
| const xmlHashEntry *entry; |
| unsigned hashValue; |
| int found; |
|
|
| if ((hash == NULL) || (hash->size == 0) || (key == NULL)) |
| return(NULL); |
| hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL); |
| entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); |
| if (found) |
| return(entry->payload); |
| return(NULL); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| void * |
| xmlHashQLookup3(xmlHashTable *hash, |
| const xmlChar *prefix, const xmlChar *name, |
| const xmlChar *prefix2, const xmlChar *name2, |
| const xmlChar *prefix3, const xmlChar *name3) { |
| const xmlHashEntry *entry; |
| unsigned hashValue, mask, pos, displ; |
|
|
| if ((hash == NULL) || (hash->size == 0) || (name == NULL)) |
| return(NULL); |
|
|
| hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2, |
| name2, prefix3, name3); |
| mask = hash->size - 1; |
| pos = hashValue & mask; |
| entry = &hash->table[pos]; |
|
|
| if (entry->hashValue != 0) { |
| displ = 0; |
| hashValue |= MAX_HASH_SIZE; |
|
|
| do { |
| if ((hashValue == entry->hashValue) && |
| (xmlStrQEqual(prefix, name, entry->key)) && |
| (xmlStrQEqual(prefix2, name2, entry->key2)) && |
| (xmlStrQEqual(prefix3, name3, entry->key3))) |
| return(entry->payload); |
|
|
| displ++; |
| pos++; |
| entry++; |
| if ((pos & mask) == 0) |
| entry = hash->table; |
| } while ((entry->hashValue != 0) && |
| (((pos - entry->hashValue) & mask) >= displ)); |
| } |
|
|
| return(NULL); |
| } |
|
|
| typedef struct { |
| xmlHashScanner scan; |
| void *data; |
| } stubData; |
|
|
| static void |
| stubHashScannerFull(void *payload, void *data, const xmlChar *key, |
| const xmlChar *key2 ATTRIBUTE_UNUSED, |
| const xmlChar *key3 ATTRIBUTE_UNUSED) { |
| stubData *sdata = (stubData *) data; |
| sdata->scan(payload, sdata->data, key); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| void |
| xmlHashScan(xmlHashTable *hash, xmlHashScanner scan, void *data) { |
| stubData sdata; |
| sdata.data = data; |
| sdata.scan = scan; |
| xmlHashScanFull(hash, stubHashScannerFull, &sdata); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| void |
| xmlHashScanFull(xmlHashTable *hash, xmlHashScannerFull scan, void *data) { |
| const xmlHashEntry *entry, *end; |
| xmlHashEntry old; |
| unsigned i; |
|
|
| if ((hash == NULL) || (hash->size == 0) || (scan == NULL)) |
| return; |
|
|
| |
| |
| |
| |
| |
| |
| |
| entry = hash->table; |
| end = &hash->table[hash->size]; |
| while (entry->hashValue != 0) { |
| if (++entry >= end) |
| entry = hash->table; |
| } |
|
|
| for (i = 0; i < hash->size; i++) { |
| if ((entry->hashValue != 0) && (entry->payload != NULL)) { |
| |
| |
| |
| do { |
| old = *entry; |
| scan(entry->payload, data, entry->key, entry->key2, entry->key3); |
| } while ((entry->hashValue != 0) && |
| (entry->payload != NULL) && |
| ((entry->key != old.key) || |
| (entry->key2 != old.key2) || |
| (entry->key3 != old.key3))); |
| } |
| if (++entry >= end) |
| entry = hash->table; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| xmlHashScan3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| xmlHashScanner scan, void *data) { |
| stubData sdata; |
| sdata.data = data; |
| sdata.scan = scan; |
| xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void |
| xmlHashScanFull3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| xmlHashScannerFull scan, void *data) { |
| const xmlHashEntry *entry, *end; |
| xmlHashEntry old; |
| unsigned i; |
|
|
| if ((hash == NULL) || (hash->size == 0) || (scan == NULL)) |
| return; |
|
|
| |
| |
| |
| |
| |
| |
| |
| entry = hash->table; |
| end = &hash->table[hash->size]; |
| while (entry->hashValue != 0) { |
| if (++entry >= end) |
| entry = hash->table; |
| } |
|
|
| for (i = 0; i < hash->size; i++) { |
| if ((entry->hashValue != 0) && (entry->payload != NULL)) { |
| |
| |
| |
| do { |
| if (((key != NULL) && (strcmp((const char *) key, |
| (const char *) entry->key) != 0)) || |
| ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) || |
| ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3)))) |
| break; |
| old = *entry; |
| scan(entry->payload, data, entry->key, entry->key2, entry->key3); |
| } while ((entry->hashValue != 0) && |
| (entry->payload != NULL) && |
| ((entry->key != old.key) || |
| (entry->key2 != old.key2) || |
| (entry->key3 != old.key3))); |
| } |
| if (++entry >= end) |
| entry = hash->table; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| xmlHashTable * |
| xmlHashCopySafe(xmlHashTable *hash, xmlHashCopier copyFunc, |
| xmlHashDeallocator deallocFunc) { |
| const xmlHashEntry *entry, *end; |
| xmlHashTablePtr ret; |
|
|
| if ((hash == NULL) || (copyFunc == NULL)) |
| return(NULL); |
|
|
| ret = xmlHashCreate(hash->size); |
| if (ret == NULL) |
| return(NULL); |
|
|
| if (hash->size == 0) |
| return(ret); |
|
|
| end = &hash->table[hash->size]; |
|
|
| for (entry = hash->table; entry < end; entry++) { |
| if (entry->hashValue != 0) { |
| void *copy; |
|
|
| copy = copyFunc(entry->payload, entry->key); |
| if (copy == NULL) |
| goto error; |
| if (xmlHashAdd3(ret, entry->key, entry->key2, entry->key3, |
| copy) <= 0) { |
| if (deallocFunc != NULL) |
| deallocFunc(copy, entry->key); |
| goto error; |
| } |
| } |
| } |
|
|
| return(ret); |
|
|
| error: |
| xmlHashFree(ret, deallocFunc); |
| return(NULL); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| xmlHashTable * |
| xmlHashCopy(xmlHashTable *hash, xmlHashCopier copy) { |
| return(xmlHashCopySafe(hash, copy, NULL)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashSize(xmlHashTable *hash) { |
| if (hash == NULL) |
| return(-1); |
| return(hash->nbElems); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int xmlHashRemoveEntry(xmlHashTable *hash, const xmlChar *key, |
| xmlHashDeallocator dealloc) { |
| return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int |
| xmlHashRemoveEntry2(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, xmlHashDeallocator dealloc) { |
| return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc)); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| int |
| xmlHashRemoveEntry3(xmlHashTable *hash, const xmlChar *key, |
| const xmlChar *key2, const xmlChar *key3, |
| xmlHashDeallocator dealloc) { |
| xmlHashEntry *entry, *cur, *next; |
| unsigned hashValue, mask, pos, nextpos; |
| int found; |
|
|
| if ((hash == NULL) || (hash->size == 0) || (key == NULL)) |
| return(-1); |
|
|
| hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL); |
| entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found); |
| if (!found) |
| return(-1); |
|
|
| if ((dealloc != NULL) && (entry->payload != NULL)) |
| dealloc(entry->payload, entry->key); |
| if (hash->dict == NULL) { |
| if (entry->key) |
| xmlFree(entry->key); |
| if (entry->key2) |
| xmlFree(entry->key2); |
| if (entry->key3) |
| xmlFree(entry->key3); |
| } |
|
|
| |
| |
| |
| |
| mask = hash->size - 1; |
| pos = entry - hash->table; |
| cur = entry; |
|
|
| while (1) { |
| nextpos = pos + 1; |
| next = cur + 1; |
| if ((nextpos & mask) == 0) |
| next = hash->table; |
|
|
| if ((next->hashValue == 0) || |
| (((next->hashValue - nextpos) & mask) == 0)) |
| break; |
|
|
| cur = next; |
| pos = nextpos; |
| } |
|
|
| |
| |
| |
| next = entry + 1; |
|
|
| if (cur < entry) { |
| xmlHashEntry *end = &hash->table[hash->size]; |
|
|
| memmove(entry, next, (char *) end - (char *) next); |
| entry = hash->table; |
| end[-1] = *entry; |
| next = entry + 1; |
| } |
|
|
| memmove(entry, next, (char *) cur - (char *) entry); |
|
|
| |
| |
| |
| cur->hashValue = 0; |
|
|
| hash->nbElems--; |
|
|
| return(0); |
| } |
|
|
|
|