2 * hash.c: chained hash tables
4 * Reference: Your favorite introductory book on algorithms
6 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard.
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
13 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
14 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
15 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
17 * Author: breese@users.sourceforge.net
24 #include <libxml/parser.h>
25 #include <libxml/hash.h>
26 #include <libxml/xmlmemory.h>
27 #include <libxml/xmlerror.h>
28 #include <libxml/globals.h>
30 #define MAX_HASH_LEN 8
32 /* #define DEBUG_GROW */
35 * A single entry in the hash table
37 typedef struct _xmlHashEntry xmlHashEntry;
38 typedef xmlHashEntry *xmlHashEntryPtr;
39 struct _xmlHashEntry {
40 struct _xmlHashEntry *next;
49 * The entire hash table
51 struct _xmlHashTable {
52 struct _xmlHashEntry *table;
60 * Calculate the hash key
63 xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
64 const xmlChar *name2, const xmlChar *name3) {
65 unsigned long value = 0L;
69 value += 30 * (*name);
70 while ((ch = *name++) != 0) {
71 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
75 while ((ch = *name2++) != 0) {
76 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
80 while ((ch = *name3++) != 0) {
81 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
84 return (value % table->size);
88 xmlHashComputeQKey(xmlHashTablePtr table,
89 const xmlChar *prefix, const xmlChar *name,
90 const xmlChar *prefix2, const xmlChar *name2,
91 const xmlChar *prefix3, const xmlChar *name3) {
92 unsigned long value = 0L;
96 value += 30 * (*prefix);
98 value += 30 * (*name);
100 if (prefix != NULL) {
101 while ((ch = *prefix++) != 0) {
102 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
104 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)':');
107 while ((ch = *name++) != 0) {
108 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
111 if (prefix2 != NULL) {
112 while ((ch = *prefix2++) != 0) {
113 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
115 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)':');
118 while ((ch = *name2++) != 0) {
119 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
122 if (prefix3 != NULL) {
123 while ((ch = *prefix3++) != 0) {
124 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
126 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)':');
129 while ((ch = *name3++) != 0) {
130 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
133 return (value % table->size);
138 * @size: the size of the hash table
140 * Create a new xmlHashTablePtr.
142 * Returns the newly created object, or NULL if an error occured.
145 xmlHashCreate(int size) {
146 xmlHashTablePtr table;
151 table = xmlMalloc(sizeof(xmlHashTable));
156 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
158 memset(table->table, 0, size * sizeof(xmlHashEntry));
168 * @size: the size of the hash table
169 * @dict: a dictionary to use for the hash
171 * Create a new xmlHashTablePtr which will use @dict as the internal dictionary
173 * Returns the newly created object, or NULL if an error occured.
176 xmlHashCreateDict(int size, xmlDictPtr dict) {
177 xmlHashTablePtr table;
179 table = xmlHashCreate(size);
182 xmlDictReference(dict);
189 * @table: the hash table
190 * @size: the new size of the hash table
192 * resize the hash table
194 * Returns 0 in case of success, -1 in case of failure
197 xmlHashGrow(xmlHashTablePtr table, int size) {
200 xmlHashEntryPtr iter, next;
201 struct _xmlHashEntry *oldtable;
203 unsigned long nbElem = 0;
213 oldsize = table->size;
214 oldtable = table->table;
215 if (oldtable == NULL)
218 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
219 if (table->table == NULL) {
220 table->table = oldtable;
223 memset(table->table, 0, size * sizeof(xmlHashEntry));
226 /* If the two loops are merged, there would be situations where
227 a new entry needs to allocated and data copied into it from
228 the main table. So instead, we run through the array twice, first
229 copying all the elements in the main array (where we can't get
230 conflicts) and then the rest, so we only free (and don't allocate)
232 for (i = 0; i < oldsize; i++) {
233 if (oldtable[i].valid == 0)
235 key = xmlHashComputeKey(table, oldtable[i].name, oldtable[i].name2,
237 memcpy(&(table->table[key]), &(oldtable[i]), sizeof(xmlHashEntry));
238 table->table[key].next = NULL;
241 for (i = 0; i < oldsize; i++) {
242 iter = oldtable[i].next;
247 * put back the entry in the new table
250 key = xmlHashComputeKey(table, iter->name, iter->name2,
252 if (table->table[key].valid == 0) {
253 memcpy(&(table->table[key]), iter, sizeof(xmlHashEntry));
254 table->table[key].next = NULL;
257 iter->next = table->table[key].next;
258 table->table[key].next = iter;
272 xmlGenericError(xmlGenericErrorContext,
273 "xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
281 * @table: the hash table
282 * @f: the deallocator function for items in the hash
284 * Free the hash @table and its contents. The userdata is
285 * deallocated with @f if provided.
288 xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
290 xmlHashEntryPtr iter;
291 xmlHashEntryPtr next;
292 int inside_table = 0;
298 nbElems = table->nbElems;
299 for(i = 0; (i < table->size) && (nbElems > 0); i++) {
300 iter = &(table->table[i]);
301 if (iter->valid == 0)
306 if ((f != NULL) && (iter->payload != NULL))
307 f(iter->payload, iter->name);
308 if (table->dict == NULL) {
312 xmlFree(iter->name2);
314 xmlFree(iter->name3);
316 iter->payload = NULL;
325 xmlFree(table->table);
328 xmlDictFree(table->dict);
334 * @table: the hash table
335 * @name: the name of the userdata
336 * @userdata: a pointer to the userdata
338 * Add the @userdata to the hash @table. This can later be retrieved
339 * by using the @name. Duplicate names generate errors.
341 * Returns 0 the addition succeeded and -1 in case of error.
344 xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
345 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
350 * @table: the hash table
351 * @name: the name of the userdata
352 * @name2: a second name of the userdata
353 * @userdata: a pointer to the userdata
355 * Add the @userdata to the hash @table. This can later be retrieved
356 * by using the (@name, @name2) tuple. Duplicate tuples generate errors.
358 * Returns 0 the addition succeeded and -1 in case of error.
361 xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
362 const xmlChar *name2, void *userdata) {
363 return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
367 * xmlHashUpdateEntry:
368 * @table: the hash table
369 * @name: the name of the userdata
370 * @userdata: a pointer to the userdata
371 * @f: the deallocator function for replaced item (if any)
373 * Add the @userdata to the hash @table. This can later be retrieved
374 * by using the @name. Existing entry for this @name will be removed
375 * and freed with @f if found.
377 * Returns 0 the addition succeeded and -1 in case of error.
380 xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
381 void *userdata, xmlHashDeallocator f) {
382 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
386 * xmlHashUpdateEntry2:
387 * @table: the hash table
388 * @name: the name of the userdata
389 * @name2: a second name of the userdata
390 * @userdata: a pointer to the userdata
391 * @f: the deallocator function for replaced item (if any)
393 * Add the @userdata to the hash @table. This can later be retrieved
394 * by using the (@name, @name2) tuple. Existing entry for this tuple will
395 * be removed and freed with @f if found.
397 * Returns 0 the addition succeeded and -1 in case of error.
400 xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
401 const xmlChar *name2, void *userdata,
402 xmlHashDeallocator f) {
403 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
408 * @table: the hash table
409 * @name: the name of the userdata
411 * Find the userdata specified by the @name.
413 * Returns the pointer to the userdata
416 xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
417 return(xmlHashLookup3(table, name, NULL, NULL));
422 * @table: the hash table
423 * @name: the name of the userdata
424 * @name2: a second name of the userdata
426 * Find the userdata specified by the (@name, @name2) tuple.
428 * Returns the pointer to the userdata
431 xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
432 const xmlChar *name2) {
433 return(xmlHashLookup3(table, name, name2, NULL));
438 * @table: the hash table
439 * @prefix: the prefix of the userdata
440 * @name: the name of the userdata
442 * Find the userdata specified by the QName @prefix:@name/@name.
444 * Returns the pointer to the userdata
447 xmlHashQLookup(xmlHashTablePtr table, const xmlChar *prefix,
448 const xmlChar *name) {
449 return(xmlHashQLookup3(table, prefix, name, NULL, NULL, NULL, NULL));
454 * @table: the hash table
455 * @prefix: the prefix of the userdata
456 * @name: the name of the userdata
457 * @prefix2: the second prefix of the userdata
458 * @name2: a second name of the userdata
460 * Find the userdata specified by the QNames tuple
462 * Returns the pointer to the userdata
465 xmlHashQLookup2(xmlHashTablePtr table, const xmlChar *prefix,
466 const xmlChar *name, const xmlChar *prefix2,
467 const xmlChar *name2) {
468 return(xmlHashQLookup3(table, prefix, name, prefix2, name2, NULL, NULL));
473 * @table: the hash table
474 * @name: the name of the userdata
475 * @name2: a second name of the userdata
476 * @name3: a third name of the userdata
477 * @userdata: a pointer to the userdata
479 * Add the @userdata to the hash @table. This can later be retrieved
480 * by using the tuple (@name, @name2, @name3). Duplicate entries generate
483 * Returns 0 the addition succeeded and -1 in case of error.
486 xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
487 const xmlChar *name2, const xmlChar *name3,
489 unsigned long key, len = 0;
490 xmlHashEntryPtr entry;
491 xmlHashEntryPtr insert;
493 if ((table == NULL) || (name == NULL))
497 * If using a dict internalize if needed
500 if (!xmlDictOwns(table->dict, name)) {
501 name = xmlDictLookup(table->dict, name, -1);
505 if ((name2 != NULL) && (!xmlDictOwns(table->dict, name2))) {
506 name2 = xmlDictLookup(table->dict, name2, -1);
510 if ((name3 != NULL) && (!xmlDictOwns(table->dict, name3))) {
511 name3 = xmlDictLookup(table->dict, name3, -1);
518 * Check for duplicate and insertion location.
520 key = xmlHashComputeKey(table, name, name2, name3);
521 if (table->table[key].valid == 0) {
525 for (insert = &(table->table[key]); insert->next != NULL;
526 insert = insert->next) {
527 if ((insert->name == name) &&
528 (insert->name2 == name2) &&
529 (insert->name3 == name3))
533 if ((insert->name == name) &&
534 (insert->name2 == name2) &&
535 (insert->name3 == name3))
538 for (insert = &(table->table[key]); insert->next != NULL;
539 insert = insert->next) {
540 if ((xmlStrEqual(insert->name, name)) &&
541 (xmlStrEqual(insert->name2, name2)) &&
542 (xmlStrEqual(insert->name3, name3)))
546 if ((xmlStrEqual(insert->name, name)) &&
547 (xmlStrEqual(insert->name2, name2)) &&
548 (xmlStrEqual(insert->name3, name3)))
553 if (insert == NULL) {
554 entry = &(table->table[key]);
556 entry = xmlMalloc(sizeof(xmlHashEntry));
561 if (table->dict != NULL) {
562 entry->name = (xmlChar *) name;
563 entry->name2 = (xmlChar *) name2;
564 entry->name3 = (xmlChar *) name3;
566 entry->name = xmlStrdup(name);
567 entry->name2 = xmlStrdup(name2);
568 entry->name3 = xmlStrdup(name3);
570 entry->payload = userdata;
576 insert->next = entry;
580 if (len > MAX_HASH_LEN)
581 xmlHashGrow(table, MAX_HASH_LEN * table->size);
587 * xmlHashUpdateEntry3:
588 * @table: the hash table
589 * @name: the name of the userdata
590 * @name2: a second name of the userdata
591 * @name3: a third name of the userdata
592 * @userdata: a pointer to the userdata
593 * @f: the deallocator function for replaced item (if any)
595 * Add the @userdata to the hash @table. This can later be retrieved
596 * by using the tuple (@name, @name2, @name3). Existing entry for this tuple
597 * will be removed and freed with @f if found.
599 * Returns 0 the addition succeeded and -1 in case of error.
602 xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
603 const xmlChar *name2, const xmlChar *name3,
604 void *userdata, xmlHashDeallocator f) {
606 xmlHashEntryPtr entry;
607 xmlHashEntryPtr insert;
609 if ((table == NULL) || name == NULL)
613 * If using a dict internalize if needed
616 if (!xmlDictOwns(table->dict, name)) {
617 name = xmlDictLookup(table->dict, name, -1);
621 if ((name2 != NULL) && (!xmlDictOwns(table->dict, name2))) {
622 name2 = xmlDictLookup(table->dict, name2, -1);
626 if ((name3 != NULL) && (!xmlDictOwns(table->dict, name3))) {
627 name3 = xmlDictLookup(table->dict, name3, -1);
634 * Check for duplicate and insertion location.
636 key = xmlHashComputeKey(table, name, name2, name3);
637 if (table->table[key].valid == 0) {
641 for (insert = &(table->table[key]); insert->next != NULL;
642 insert = insert->next) {
643 if ((insert->name == name) &&
644 (insert->name2 == name2) &&
645 (insert->name3 == name3)) {
647 f(insert->payload, insert->name);
648 insert->payload = userdata;
652 if ((insert->name == name) &&
653 (insert->name2 == name2) &&
654 (insert->name3 == name3)) {
656 f(insert->payload, insert->name);
657 insert->payload = userdata;
661 for (insert = &(table->table[key]); insert->next != NULL;
662 insert = insert->next) {
663 if ((xmlStrEqual(insert->name, name)) &&
664 (xmlStrEqual(insert->name2, name2)) &&
665 (xmlStrEqual(insert->name3, name3))) {
667 f(insert->payload, insert->name);
668 insert->payload = userdata;
672 if ((xmlStrEqual(insert->name, name)) &&
673 (xmlStrEqual(insert->name2, name2)) &&
674 (xmlStrEqual(insert->name3, name3))) {
676 f(insert->payload, insert->name);
677 insert->payload = userdata;
683 if (insert == NULL) {
684 entry = &(table->table[key]);
686 entry = xmlMalloc(sizeof(xmlHashEntry));
691 if (table->dict != NULL) {
692 entry->name = (xmlChar *) name;
693 entry->name2 = (xmlChar *) name2;
694 entry->name3 = (xmlChar *) name3;
696 entry->name = xmlStrdup(name);
697 entry->name2 = xmlStrdup(name2);
698 entry->name3 = xmlStrdup(name3);
700 entry->payload = userdata;
706 if (insert != NULL) {
707 insert->next = entry;
714 * @table: the hash table
715 * @name: the name of the userdata
716 * @name2: a second name of the userdata
717 * @name3: a third name of the userdata
719 * Find the userdata specified by the (@name, @name2, @name3) tuple.
721 * Returns the a pointer to the userdata
724 xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
725 const xmlChar *name2, const xmlChar *name3) {
727 xmlHashEntryPtr entry;
733 key = xmlHashComputeKey(table, name, name2, name3);
734 if (table->table[key].valid == 0)
737 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
738 if ((entry->name == name) &&
739 (entry->name2 == name2) &&
740 (entry->name3 == name3))
741 return(entry->payload);
744 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
745 if ((xmlStrEqual(entry->name, name)) &&
746 (xmlStrEqual(entry->name2, name2)) &&
747 (xmlStrEqual(entry->name3, name3)))
748 return(entry->payload);
755 * @table: the hash table
756 * @prefix: the prefix of the userdata
757 * @name: the name of the userdata
758 * @prefix2: the second prefix of the userdata
759 * @name2: a second name of the userdata
760 * @prefix3: the third prefix of the userdata
761 * @name3: a third name of the userdata
763 * Find the userdata specified by the (@name, @name2, @name3) tuple.
765 * Returns the a pointer to the userdata
768 xmlHashQLookup3(xmlHashTablePtr table,
769 const xmlChar *prefix, const xmlChar *name,
770 const xmlChar *prefix2, const xmlChar *name2,
771 const xmlChar *prefix3, const xmlChar *name3) {
773 xmlHashEntryPtr entry;
779 key = xmlHashComputeQKey(table, prefix, name, prefix2,
780 name2, prefix3, name3);
781 if (table->table[key].valid == 0)
783 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
784 if ((xmlStrQEqual(prefix, name, entry->name)) &&
785 (xmlStrQEqual(prefix2, name2, entry->name2)) &&
786 (xmlStrQEqual(prefix3, name3, entry->name3)))
787 return(entry->payload);
793 xmlHashScanner hashscanner;
798 stubHashScannerFull (void *payload, void *data, const xmlChar *name,
799 const xmlChar *name2 ATTRIBUTE_UNUSED,
800 const xmlChar *name3 ATTRIBUTE_UNUSED) {
801 stubData *stubdata = (stubData *) data;
802 stubdata->hashscanner (payload, stubdata->data, (xmlChar *) name);
807 * @table: the hash table
808 * @f: the scanner function for items in the hash
809 * @data: extra data passed to f
811 * Scan the hash @table and applied @f to each value.
814 xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
816 stubdata.data = data;
817 stubdata.hashscanner = f;
818 xmlHashScanFull (table, stubHashScannerFull, &stubdata);
823 * @table: the hash table
824 * @f: the scanner function for items in the hash
825 * @data: extra data passed to f
827 * Scan the hash @table and applied @f to each value.
830 xmlHashScanFull(xmlHashTablePtr table, xmlHashScannerFull f, void *data) {
832 xmlHashEntryPtr iter;
833 xmlHashEntryPtr next;
841 for(i = 0; i < table->size; i++) {
842 if (table->table[i].valid == 0)
844 iter = &(table->table[i]);
847 if ((f != NULL) && (iter->payload != NULL))
848 f(iter->payload, data, iter->name,
849 iter->name2, iter->name3);
858 * @table: the hash table
859 * @name: the name of the userdata or NULL
860 * @name2: a second name of the userdata or NULL
861 * @name3: a third name of the userdata or NULL
862 * @f: the scanner function for items in the hash
863 * @data: extra data passed to f
865 * Scan the hash @table and applied @f to each value matching
866 * (@name, @name2, @name3) tuple. If one of the names is null,
867 * the comparison is considered to match.
870 xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
871 const xmlChar *name2, const xmlChar *name3,
872 xmlHashScanner f, void *data) {
873 xmlHashScanFull3 (table, name, name2, name3,
874 (xmlHashScannerFull) f, data);
879 * @table: the hash table
880 * @name: the name of the userdata or NULL
881 * @name2: a second name of the userdata or NULL
882 * @name3: a third name of the userdata or NULL
883 * @f: the scanner function for items in the hash
884 * @data: extra data passed to f
886 * Scan the hash @table and applied @f to each value matching
887 * (@name, @name2, @name3) tuple. If one of the names is null,
888 * the comparison is considered to match.
891 xmlHashScanFull3(xmlHashTablePtr table, const xmlChar *name,
892 const xmlChar *name2, const xmlChar *name3,
893 xmlHashScannerFull f, void *data) {
895 xmlHashEntryPtr iter;
896 xmlHashEntryPtr next;
904 for(i = 0; i < table->size; i++) {
905 if (table->table[i].valid == 0)
907 iter = &(table->table[i]);
910 if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
911 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
912 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3))) &&
913 (iter->payload != NULL)) {
914 f(iter->payload, data, iter->name,
915 iter->name2, iter->name3);
925 * @table: the hash table
926 * @f: the copier function for items in the hash
928 * Scan the hash @table and applied @f to each value.
930 * Returns the new table or NULL in case of error.
933 xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
935 xmlHashEntryPtr iter;
936 xmlHashEntryPtr next;
944 ret = xmlHashCreate(table->size);
946 for(i = 0; i < table->size; i++) {
947 if (table->table[i].valid == 0)
949 iter = &(table->table[i]);
952 xmlHashAddEntry3(ret, iter->name, iter->name2,
953 iter->name3, f(iter->payload, iter->name));
958 ret->nbElems = table->nbElems;
964 * @table: the hash table
966 * Query the number of elements installed in the hash @table.
968 * Returns the number of elements in the hash table or
969 * -1 in case of error
972 xmlHashSize(xmlHashTablePtr table) {
975 return(table->nbElems);
979 * xmlHashRemoveEntry:
980 * @table: the hash table
981 * @name: the name of the userdata
982 * @f: the deallocator function for removed item (if any)
984 * Find the userdata specified by the @name and remove
985 * it from the hash @table. Existing userdata for this tuple will be removed
988 * Returns 0 if the removal succeeded and -1 in case of error or not found.
990 int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
991 xmlHashDeallocator f) {
992 return(xmlHashRemoveEntry3(table, name, NULL, NULL, f));
996 * xmlHashRemoveEntry2:
997 * @table: the hash table
998 * @name: the name of the userdata
999 * @name2: a second name of the userdata
1000 * @f: the deallocator function for removed item (if any)
1002 * Find the userdata specified by the (@name, @name2) tuple and remove
1003 * it from the hash @table. Existing userdata for this tuple will be removed
1004 * and freed with @f.
1006 * Returns 0 if the removal succeeded and -1 in case of error or not found.
1009 xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
1010 const xmlChar *name2, xmlHashDeallocator f) {
1011 return(xmlHashRemoveEntry3(table, name, name2, NULL, f));
1015 * xmlHashRemoveEntry3:
1016 * @table: the hash table
1017 * @name: the name of the userdata
1018 * @name2: a second name of the userdata
1019 * @name3: a third name of the userdata
1020 * @f: the deallocator function for removed item (if any)
1022 * Find the userdata specified by the (@name, @name2, @name3) tuple and remove
1023 * it from the hash @table. Existing userdata for this tuple will be removed
1024 * and freed with @f.
1026 * Returns 0 if the removal succeeded and -1 in case of error or not found.
1029 xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
1030 const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
1032 xmlHashEntryPtr entry;
1033 xmlHashEntryPtr prev = NULL;
1035 if (table == NULL || name == NULL)
1038 key = xmlHashComputeKey(table, name, name2, name3);
1039 if (table->table[key].valid == 0) {
1042 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
1043 if (xmlStrEqual(entry->name, name) &&
1044 xmlStrEqual(entry->name2, name2) &&
1045 xmlStrEqual(entry->name3, name3)) {
1046 if ((f != NULL) && (entry->payload != NULL))
1047 f(entry->payload, entry->name);
1048 entry->payload = NULL;
1049 if (table->dict == NULL) {
1051 xmlFree(entry->name);
1053 xmlFree(entry->name2);
1055 xmlFree(entry->name3);
1058 prev->next = entry->next;
1061 if (entry->next == NULL) {
1064 entry = entry->next;
1065 memcpy(&(table->table[key]), entry, sizeof(xmlHashEntry));
1079 #include "elfgcchack.h"