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/hash.h>
25 #include <libxml/xmlmemory.h>
26 #include <libxml/parser.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;
59 * Calculate the hash key
62 xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
63 const xmlChar *name2, const xmlChar *name3) {
64 unsigned long value = 0L;
68 value += 30 * (*name);
69 while ((ch = *name++) != 0) {
70 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
74 while ((ch = *name2++) != 0) {
75 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
79 while ((ch = *name3++) != 0) {
80 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
83 return (value % table->size);
88 * @size: the size of the hash table
90 * Create a new xmlHashTablePtr.
92 * Returns the newly created object, or NULL if an error occured.
95 xmlHashCreate(int size) {
96 xmlHashTablePtr table;
101 table = xmlMalloc(sizeof(xmlHashTable));
105 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
107 memset(table->table, 0, size * sizeof(xmlHashEntry));
117 * @table: the hash table
118 * @size: the new size of the hash table
120 * resize the hash table
122 * Returns 0 in case of success, -1 in case of failure
125 xmlHashGrow(xmlHashTablePtr table, int size) {
128 xmlHashEntryPtr iter, next;
129 struct _xmlHashEntry *oldtable;
131 unsigned long nbElem = 0;
141 oldsize = table->size;
142 oldtable = table->table;
143 if (oldtable == NULL)
146 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
147 if (table->table == NULL) {
148 table->table = oldtable;
151 memset(table->table, 0, size * sizeof(xmlHashEntry));
154 /* If the two loops are merged, there would be situations where
155 a new entry needs to allocated and data copied into it from
156 the main table. So instead, we run through the array twice, first
157 copying all the elements in the main array (where we can't get
158 conflicts) and then the rest, so we only free (and don't allocate)
160 for (i = 0; i < oldsize; i++) {
161 if (oldtable[i].valid == 0)
163 key = xmlHashComputeKey(table, oldtable[i].name, oldtable[i].name2,
165 memcpy(&(table->table[key]), &(oldtable[i]), sizeof(xmlHashEntry));
166 table->table[key].next = NULL;
169 for (i = 0; i < oldsize; i++) {
170 iter = oldtable[i].next;
175 * put back the entry in the new table
178 key = xmlHashComputeKey(table, iter->name, iter->name2,
180 if (table->table[key].valid == 0) {
181 memcpy(&(table->table[key]), iter, sizeof(xmlHashEntry));
182 table->table[key].next = NULL;
185 iter->next = table->table[key].next;
186 table->table[key].next = iter;
200 xmlGenericError(xmlGenericErrorContext,
201 "xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
209 * @table: the hash table
210 * @f: the deallocator function for items in the hash
212 * Free the hash @table and its contents. The userdata is
213 * deallocated with @f if provided.
216 xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
218 xmlHashEntryPtr iter;
219 xmlHashEntryPtr next;
220 int inside_table = 0;
225 for(i = 0; i < table->size; i++) {
226 iter = &(table->table[i]);
227 if (iter->valid == 0)
233 f(iter->payload, iter->name);
237 xmlFree(iter->name2);
239 xmlFree(iter->name3);
240 iter->payload = NULL;
248 xmlFree(table->table);
255 * @table: the hash table
256 * @name: the name of the userdata
257 * @userdata: a pointer to the userdata
259 * Add the @userdata to the hash @table. This can later be retrieved
260 * by using the @name. Duplicate names generate errors.
262 * Returns 0 the addition succeeded and -1 in case of error.
265 xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
266 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
271 * @table: the hash table
272 * @name: the name of the userdata
273 * @name2: a second name of the userdata
274 * @userdata: a pointer to the userdata
276 * Add the @userdata to the hash @table. This can later be retrieved
277 * by using the (@name, @name2) tuple. Duplicate tuples generate errors.
279 * Returns 0 the addition succeeded and -1 in case of error.
282 xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
283 const xmlChar *name2, void *userdata) {
284 return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
288 * xmlHashUpdateEntry:
289 * @table: the hash table
290 * @name: the name of the userdata
291 * @userdata: a pointer to the userdata
292 * @f: the deallocator function for replaced item (if any)
294 * Add the @userdata to the hash @table. This can later be retrieved
295 * by using the @name. Existing entry for this @name will be removed
296 * and freed with @f if found.
298 * Returns 0 the addition succeeded and -1 in case of error.
301 xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
302 void *userdata, xmlHashDeallocator f) {
303 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
307 * xmlHashUpdateEntry2:
308 * @table: the hash table
309 * @name: the name of the userdata
310 * @name2: a second name of the userdata
311 * @userdata: a pointer to the userdata
312 * @f: the deallocator function for replaced item (if any)
314 * Add the @userdata to the hash @table. This can later be retrieved
315 * by using the (@name, @name2) tuple. Existing entry for this tuple will
316 * be removed and freed with @f if found.
318 * Returns 0 the addition succeeded and -1 in case of error.
321 xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
322 const xmlChar *name2, void *userdata,
323 xmlHashDeallocator f) {
324 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
329 * @table: the hash table
330 * @name: the name of the userdata
332 * Find the userdata specified by the @name.
334 * Returns the pointer to the userdata
337 xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
338 return(xmlHashLookup3(table, name, NULL, NULL));
343 * @table: the hash table
344 * @name: the name of the userdata
345 * @name2: a second name of the userdata
347 * Find the userdata specified by the (@name, @name2) tuple.
349 * Returns the pointer to the userdata
352 xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
353 const xmlChar *name2) {
354 return(xmlHashLookup3(table, name, name2, NULL));
359 * @table: the hash table
360 * @name: the name of the userdata
361 * @name2: a second name of the userdata
362 * @name3: a third name of the userdata
363 * @userdata: a pointer to the userdata
365 * Add the @userdata to the hash @table. This can later be retrieved
366 * by using the tuple (@name, @name2, @name3). Duplicate entries generate
369 * Returns 0 the addition succeeded and -1 in case of error.
372 xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
373 const xmlChar *name2, const xmlChar *name3,
375 unsigned long key, len = 0;
376 xmlHashEntryPtr entry;
377 xmlHashEntryPtr insert;
379 if ((table == NULL) || name == NULL)
383 * Check for duplicate and insertion location.
385 key = xmlHashComputeKey(table, name, name2, name3);
386 if (table->table[key].valid == 0) {
389 for (insert = &(table->table[key]); insert->next != NULL;
390 insert = insert->next) {
391 if ((xmlStrEqual(insert->name, name)) &&
392 (xmlStrEqual(insert->name2, name2)) &&
393 (xmlStrEqual(insert->name3, name3)))
397 if ((xmlStrEqual(insert->name, name)) &&
398 (xmlStrEqual(insert->name2, name2)) &&
399 (xmlStrEqual(insert->name3, name3)))
403 if (insert == NULL) {
404 entry = &(table->table[key]);
406 entry = xmlMalloc(sizeof(xmlHashEntry));
411 entry->name = xmlStrdup(name);
412 entry->name2 = xmlStrdup(name2);
413 entry->name3 = xmlStrdup(name3);
414 entry->payload = userdata;
420 insert->next = entry;
424 if (len > MAX_HASH_LEN)
425 xmlHashGrow(table, MAX_HASH_LEN * table->size);
431 * xmlHashUpdateEntry3:
432 * @table: the hash table
433 * @name: the name of the userdata
434 * @name2: a second name of the userdata
435 * @name3: a third name of the userdata
436 * @userdata: a pointer to the userdata
437 * @f: the deallocator function for replaced item (if any)
439 * Add the @userdata to the hash @table. This can later be retrieved
440 * by using the tuple (@name, @name2, @name3). Existing entry for this tuple
441 * will be removed and freed with @f if found.
443 * Returns 0 the addition succeeded and -1 in case of error.
446 xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
447 const xmlChar *name2, const xmlChar *name3,
448 void *userdata, xmlHashDeallocator f) {
450 xmlHashEntryPtr entry;
451 xmlHashEntryPtr insert;
453 if ((table == NULL) || name == NULL)
457 * Check for duplicate and insertion location.
459 key = xmlHashComputeKey(table, name, name2, name3);
460 if (table->table[key].valid == 0) {
463 for (insert = &(table->table[key]); insert->next != NULL;
464 insert = insert->next) {
465 if ((xmlStrEqual(insert->name, name)) &&
466 (xmlStrEqual(insert->name2, name2)) &&
467 (xmlStrEqual(insert->name3, name3))) {
469 f(insert->payload, insert->name);
470 insert->payload = userdata;
474 if ((xmlStrEqual(insert->name, name)) &&
475 (xmlStrEqual(insert->name2, name2)) &&
476 (xmlStrEqual(insert->name3, name3))) {
478 f(insert->payload, insert->name);
479 insert->payload = userdata;
484 if (insert == NULL) {
485 entry = &(table->table[key]);
487 entry = xmlMalloc(sizeof(xmlHashEntry));
492 entry->name = xmlStrdup(name);
493 entry->name2 = xmlStrdup(name2);
494 entry->name3 = xmlStrdup(name3);
495 entry->payload = userdata;
501 if (insert != NULL) {
502 insert->next = entry;
509 * @table: the hash table
510 * @name: the name of the userdata
511 * @name2: a second name of the userdata
512 * @name3: a third name of the userdata
514 * Find the userdata specified by the (@name, @name2, @name3) tuple.
516 * Returns the a pointer to the userdata
519 xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
520 const xmlChar *name2, const xmlChar *name3) {
522 xmlHashEntryPtr entry;
528 key = xmlHashComputeKey(table, name, name2, name3);
529 if (table->table[key].valid == 0)
531 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
532 if ((xmlStrEqual(entry->name, name)) &&
533 (xmlStrEqual(entry->name2, name2)) &&
534 (xmlStrEqual(entry->name3, name3)))
535 return(entry->payload);
541 xmlHashScanner hashscanner;
546 stubHashScannerFull (void *payload, void *data, const xmlChar *name,
547 const xmlChar *name2 ATTRIBUTE_UNUSED,
548 const xmlChar *name3 ATTRIBUTE_UNUSED) {
549 stubData *stubdata = (stubData *) data;
550 stubdata->hashscanner (payload, stubdata->data, (xmlChar *) name);
555 * @table: the hash table
556 * @f: the scanner function for items in the hash
557 * @data: extra data passed to f
559 * Scan the hash @table and applied @f to each value.
562 xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
564 stubdata.data = data;
565 stubdata.hashscanner = f;
566 xmlHashScanFull (table, stubHashScannerFull, &stubdata);
571 * @table: the hash table
572 * @f: the scanner function for items in the hash
573 * @data: extra data passed to f
575 * Scan the hash @table and applied @f to each value.
578 xmlHashScanFull(xmlHashTablePtr table, xmlHashScannerFull f, void *data) {
580 xmlHashEntryPtr iter;
581 xmlHashEntryPtr next;
589 for(i = 0; i < table->size; i++) {
590 if (table->table[i].valid == 0)
592 iter = &(table->table[i]);
596 f(iter->payload, data, iter->name,
597 iter->name2, iter->name3);
606 * @table: the hash table
607 * @name: the name of the userdata or NULL
608 * @name2: a second name of the userdata or NULL
609 * @name3: a third name of the userdata or NULL
610 * @f: the scanner function for items in the hash
611 * @data: extra data passed to f
613 * Scan the hash @table and applied @f to each value matching
614 * (@name, @name2, @name3) tuple. If one of the names is null,
615 * the comparison is considered to match.
618 xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
619 const xmlChar *name2, const xmlChar *name3,
620 xmlHashScanner f, void *data) {
621 xmlHashScanFull3 (table, name, name2, name3,
622 (xmlHashScannerFull) f, data);
627 * @table: the hash table
628 * @name: the name of the userdata or NULL
629 * @name2: a second name of the userdata or NULL
630 * @name3: a third name of the userdata or NULL
631 * @f: the scanner function for items in the hash
632 * @data: extra data passed to f
634 * Scan the hash @table and applied @f to each value matching
635 * (@name, @name2, @name3) tuple. If one of the names is null,
636 * the comparison is considered to match.
639 xmlHashScanFull3(xmlHashTablePtr table, const xmlChar *name,
640 const xmlChar *name2, const xmlChar *name3,
641 xmlHashScannerFull f, void *data) {
643 xmlHashEntryPtr iter;
644 xmlHashEntryPtr next;
652 for(i = 0; i < table->size; i++) {
653 if (table->table[i].valid == 0)
655 iter = &(table->table[i]);
658 if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
659 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
660 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3)))) {
661 f(iter->payload, data, iter->name,
662 iter->name2, iter->name3);
672 * @table: the hash table
673 * @f: the copier function for items in the hash
675 * Scan the hash @table and applied @f to each value.
677 * Returns the new table or NULL in case of error.
680 xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
682 xmlHashEntryPtr iter;
683 xmlHashEntryPtr next;
691 ret = xmlHashCreate(table->size);
693 for(i = 0; i < table->size; i++) {
694 if (table->table[i].valid == 0)
696 iter = &(table->table[i]);
699 xmlHashAddEntry3(ret, iter->name, iter->name2,
700 iter->name3, f(iter->payload, iter->name));
705 ret->nbElems = table->nbElems;
711 * @table: the hash table
713 * Query the number of elements installed in the hash @table.
715 * Returns the number of elements in the hash table or
716 * -1 in case of error
719 xmlHashSize(xmlHashTablePtr table) {
722 return(table->nbElems);
726 * xmlHashRemoveEntry:
727 * @table: the hash table
728 * @name: the name of the userdata
729 * @f: the deallocator function for removed item (if any)
731 * Find the userdata specified by the @name and remove
732 * it from the hash @table. Existing userdata for this tuple will be removed
735 * Returns 0 if the removal succeeded and -1 in case of error or not found.
737 int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
738 xmlHashDeallocator f) {
739 return(xmlHashRemoveEntry3(table, name, NULL, NULL, f));
743 * xmlHashRemoveEntry2:
744 * @table: the hash table
745 * @name: the name of the userdata
746 * @name2: a second name of the userdata
747 * @f: the deallocator function for removed item (if any)
749 * Find the userdata specified by the (@name, @name2) tuple and remove
750 * it from the hash @table. Existing userdata for this tuple will be removed
753 * Returns 0 if the removal succeeded and -1 in case of error or not found.
756 xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
757 const xmlChar *name2, xmlHashDeallocator f) {
758 return(xmlHashRemoveEntry3(table, name, name2, NULL, f));
762 * xmlHashRemoveEntry3:
763 * @table: the hash table
764 * @name: the name of the userdata
765 * @name2: a second name of the userdata
766 * @name3: a third name of the userdata
767 * @f: the deallocator function for removed item (if any)
769 * Find the userdata specified by the (@name, @name2, @name3) tuple and remove
770 * it from the hash @table. Existing userdata for this tuple will be removed
773 * Returns 0 if the removal succeeded and -1 in case of error or not found.
776 xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
777 const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
779 xmlHashEntryPtr entry;
780 xmlHashEntryPtr prev = NULL;
782 if (table == NULL || name == NULL)
785 key = xmlHashComputeKey(table, name, name2, name3);
786 if (table->table[key].valid == 0) {
789 for (entry = &(table->table[key]); entry != NULL; entry = entry->next) {
790 if (xmlStrEqual(entry->name, name) &&
791 xmlStrEqual(entry->name2, name2) &&
792 xmlStrEqual(entry->name3, name3)) {
794 f(entry->payload, entry->name);
795 entry->payload = NULL;
797 xmlFree(entry->name);
799 xmlFree(entry->name2);
801 xmlFree(entry->name3);
803 prev->next = entry->next;
806 if (entry->next == NULL) {
810 memcpy(&(table->table[key]), entry, sizeof(xmlHashEntry));