blob: f8865e866e8d65c7c4c2ac3e9fe762392d92f3e3 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * hash.c: chained hash tables
3 *
4 * Reference: Your favorite introductory book on algorithms
5 *
6 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard.
7 *
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.
11 *
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.
16 *
17 * Author: bjorn.reese@systematic.dk
18 */
19
20#ifdef WIN32
21#include "win32config.h"
22#else
23#include "config.h"
24#endif
25
26#include <string.h>
27#include <libxml/hash.h>
28#include <libxml/xmlmemory.h>
29#include <libxml/parser.h>
Daniel Veillarda10efa82001-04-18 13:09:01 +000030#include <libxml/xmlerror.h>
31
32#define MAX_HASH_LEN 8
33
34/* #define DEBUG_GROW */
Owen Taylor3473f882001-02-23 17:55:21 +000035
36/*
37 * A single entry in the hash table
38 */
39typedef struct _xmlHashEntry xmlHashEntry;
40typedef xmlHashEntry *xmlHashEntryPtr;
41struct _xmlHashEntry {
42 struct _xmlHashEntry *next;
43 xmlChar *name;
44 xmlChar *name2;
45 xmlChar *name3;
46 void *payload;
47};
48
49/*
50 * The entire hash table
51 */
52struct _xmlHashTable {
53 struct _xmlHashEntry **table;
54 int size;
55 int nbElems;
56};
57
58/*
59 * xmlHashComputeKey:
60 * Calculate the hash key
61 */
62static unsigned long
Daniel Veillarda10efa82001-04-18 13:09:01 +000063xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
64 const xmlChar *name2, const xmlChar *name3) {
Owen Taylor3473f882001-02-23 17:55:21 +000065 unsigned long value = 0L;
66 char ch;
67
Daniel Veillarda10efa82001-04-18 13:09:01 +000068 if (name != NULL) {
69 value += 30 * (*name);
70 while ((ch = *name++) != 0) {
71 /* value *= 31; */
72 value += (unsigned long)ch;
73 }
74 }
75 if (name2 != NULL) {
76 while ((ch = *name2++) != 0) {
77 /* value *= 31; */
78 value += (unsigned long)ch;
79 }
80 }
81 if (name3 != NULL) {
82 while ((ch = *name3++) != 0) {
83 /* value *= 31; */
84 value += (unsigned long)ch;
85 }
Owen Taylor3473f882001-02-23 17:55:21 +000086 }
87 return (value % table->size);
88}
89
90/**
91 * xmlHashCreate:
92 * @size: the size of the hash table
93 *
94 * Create a new xmlHashTablePtr.
95 *
96 * Returns the newly created object, or NULL if an error occured.
97 */
98xmlHashTablePtr
99xmlHashCreate(int size) {
100 xmlHashTablePtr table;
101
102 if (size <= 0)
103 size = 256;
104
105 table = xmlMalloc(sizeof(xmlHashTable));
106 if (table) {
107 table->size = size;
108 table->nbElems = 0;
109 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
110 if (table->table) {
111 memset(table->table, 0, size * sizeof(xmlHashEntry));
112 return(table);
113 }
114 xmlFree(table);
115 }
116 return(NULL);
117}
118
119/**
Daniel Veillarda10efa82001-04-18 13:09:01 +0000120 * xmlHashGrow:
121 * @table: the hash table
122 * @size: the new size of the hash table
123 *
124 * resize the hash table
125 *
126 * Returns 0 in case of success, -1 in case of failure
127 */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000128static int
Daniel Veillarda10efa82001-04-18 13:09:01 +0000129xmlHashGrow(xmlHashTablePtr table, int size) {
130 unsigned long key;
131 int oldsize, i;
132 xmlHashEntryPtr iter, next;
133 struct _xmlHashEntry **oldtable;
134#ifdef DEBUG_GROW
135 unsigned long nbElem = 0;
136#endif
137
138 if (table == NULL)
139 return(-1);
140 if (size < 8)
141 return(-1);
142 if (size > 8 * 2048)
143 return(-1);
144
145 oldsize = table->size;
146 oldtable = table->table;
147 if (oldtable == NULL)
148 return(-1);
149
150 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
151 if (table->table == NULL) {
152 table->table = oldtable;
153 return(-1);
154 }
155 memset(table->table, 0, size * sizeof(xmlHashEntry));
156 table->size = size;
157
158 for (i = 0; i < oldsize; i++) {
159 iter = oldtable[i];
160 while (iter) {
161 next = iter->next;
162
163 /*
164 * put back the entry in the new table
165 */
166
167 key = xmlHashComputeKey(table, iter->name, iter->name2,
168 iter->name3);
169 iter->next = table->table[key];
170 table->table[key] = iter;
171
172#ifdef DEBUG_GROW
173 nbElem++;
174#endif
175
176 iter = next;
177 }
178 }
179
180 xmlFree(oldtable);
181
182#ifdef DEBUG_GROW
183 xmlGenericError(xmlGenericErrorContext,
184 "xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
185#endif
186
187 return(0);
188}
189
190/**
Owen Taylor3473f882001-02-23 17:55:21 +0000191 * xmlHashFree:
192 * @table: the hash table
193 * @f: the deallocator function for items in the hash
194 *
195 * Free the hash table and its contents. The userdata is
196 * deallocated with f if provided.
197 */
198void
199xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
200 int i;
201 xmlHashEntryPtr iter;
202 xmlHashEntryPtr next;
203
204 if (table == NULL)
205 return;
206 if (table->table) {
207 for(i = 0; i < table->size; i++) {
208 iter = table->table[i];
209 while (iter) {
210 next = iter->next;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000211 if (f)
212 f(iter->payload, iter->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000213 if (iter->name)
214 xmlFree(iter->name);
215 if (iter->name2)
216 xmlFree(iter->name2);
217 if (iter->name3)
218 xmlFree(iter->name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000219 iter->payload = NULL;
220 xmlFree(iter);
221 iter = next;
222 }
223 table->table[i] = NULL;
224 }
225 xmlFree(table->table);
226 }
227 xmlFree(table);
228}
229
230/**
231 * xmlHashAddEntry:
232 * @table: the hash table
233 * @name: the name of the userdata
234 * @userdata: a pointer to the userdata
235 *
236 * Add the userdata to the hash table. This can later be retrieved
237 * by using the name. Duplicate names generate errors.
238 *
239 * Returns 0 the addition succeeded and -1 in case of error.
240 */
241int
242xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
243 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
244}
245
246/**
247 * xmlHashAddEntry2:
248 * @table: the hash table
249 * @name: the name of the userdata
250 * @name2: a second name of the userdata
251 * @userdata: a pointer to the userdata
252 *
253 * Add the userdata to the hash table. This can later be retrieved
254 * by using the (name, name2) tuple. Duplicate tuples generate errors.
255 *
256 * Returns 0 the addition succeeded and -1 in case of error.
257 */
258int
259xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
260 const xmlChar *name2, void *userdata) {
261 return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
262}
263
264/**
265 * xmlHashUpdateEntry:
266 * @table: the hash table
267 * @name: the name of the userdata
268 * @userdata: a pointer to the userdata
269 * @f: the deallocator function for replaced item (if any)
270 *
271 * Add the userdata to the hash table. This can later be retrieved
272 * by using the name. Existing entry for this name will be removed
273 * and freed with @f if found.
274 *
275 * Returns 0 the addition succeeded and -1 in case of error.
276 */
277int
278xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
279 void *userdata, xmlHashDeallocator f) {
280 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
281}
282
283/**
284 * xmlHashUpdateEntry2:
285 * @table: the hash table
286 * @name: the name of the userdata
287 * @name2: a second name of the userdata
288 * @userdata: a pointer to the userdata
289 * @f: the deallocator function for replaced item (if any)
290 *
291 * Add the userdata to the hash table. This can later be retrieved
292 * by using the (name, name2) tuple. Existing entry for this tuple will
293 * be removed and freed with @f if found.
294 *
295 * Returns 0 the addition succeeded and -1 in case of error.
296 */
297int
298xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
299 const xmlChar *name2, void *userdata,
300 xmlHashDeallocator f) {
301 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
302}
303
304/**
305 * xmlHashLookup:
306 * @table: the hash table
307 * @name: the name of the userdata
308 *
309 * Find the userdata specified by the name.
310 *
311 * Returns the a pointer to the userdata
312 */
313void *
314xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
315 return(xmlHashLookup3(table, name, NULL, NULL));
316}
317
318/**
319 * xmlHashLookup2:
320 * @table: the hash table
321 * @name: the name of the userdata
322 * @name2: a second name of the userdata
323 *
324 * Find the userdata specified by the (name, name2) tuple.
325 *
326 * Returns the a pointer to the userdata
327 */
328void *
329xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
330 const xmlChar *name2) {
331 return(xmlHashLookup3(table, name, name2, NULL));
332}
333
334/**
335 * xmlHashAddEntry3:
336 * @table: the hash table
337 * @name: the name of the userdata
338 * @name2: a second name of the userdata
339 * @name3: a third name of the userdata
340 * @userdata: a pointer to the userdata
341 *
342 * Add the userdata to the hash table. This can later be retrieved
343 * by using the tuple (name, name2, name3). Duplicate entries generate
344 * errors.
345 *
346 * Returns 0 the addition succeeded and -1 in case of error.
347 */
348int
349xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
350 const xmlChar *name2, const xmlChar *name3,
351 void *userdata) {
Daniel Veillarda10efa82001-04-18 13:09:01 +0000352 unsigned long key, len = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000353 xmlHashEntryPtr entry;
354 xmlHashEntryPtr insert;
355
356 if ((table == NULL) || name == NULL)
357 return(-1);
358
359 /*
360 * Check for duplicate and insertion location.
361 */
Daniel Veillarda10efa82001-04-18 13:09:01 +0000362 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000363 if (table->table[key] == NULL) {
364 insert = NULL;
365 } else {
366 for (insert = table->table[key]; insert->next != NULL;
367 insert = insert->next) {
368 if ((xmlStrEqual(insert->name, name)) &&
369 (xmlStrEqual(insert->name2, name2)) &&
370 (xmlStrEqual(insert->name3, name3)))
371 return(-1);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000372 len++;
Owen Taylor3473f882001-02-23 17:55:21 +0000373 }
374 if ((xmlStrEqual(insert->name, name)) &&
375 (xmlStrEqual(insert->name2, name2)) &&
376 (xmlStrEqual(insert->name3, name3)))
377 return(-1);
378 }
379
380 entry = xmlMalloc(sizeof(xmlHashEntry));
381 if (entry == NULL)
382 return(-1);
383 entry->name = xmlStrdup(name);
384 entry->name2 = xmlStrdup(name2);
385 entry->name3 = xmlStrdup(name3);
386 entry->payload = userdata;
387 entry->next = NULL;
388
389
390 if (insert == NULL) {
391 table->table[key] = entry;
392 } else {
393 insert->next = entry;
394 }
395 table->nbElems++;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000396
397 if (len > MAX_HASH_LEN)
398 xmlHashGrow(table, MAX_HASH_LEN * table->size);
399
Owen Taylor3473f882001-02-23 17:55:21 +0000400 return(0);
401}
402
403/**
404 * xmlHashUpdateEntry3:
405 * @table: the hash table
406 * @name: the name of the userdata
407 * @name2: a second name of the userdata
408 * @name3: a third name of the userdata
409 * @userdata: a pointer to the userdata
410 * @f: the deallocator function for replaced item (if any)
411 *
412 * Add the userdata to the hash table. This can later be retrieved
413 * by using the tuple (name, name2, name3). Existing entry for this tuple
414 * will be removed and freed with @f if found.
415 *
416 * Returns 0 the addition succeeded and -1 in case of error.
417 */
418int
419xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
420 const xmlChar *name2, const xmlChar *name3,
421 void *userdata, xmlHashDeallocator f) {
422 unsigned long key;
423 xmlHashEntryPtr entry;
424 xmlHashEntryPtr insert;
425
426 if ((table == NULL) || name == NULL)
427 return(-1);
428
429 /*
430 * Check for duplicate and insertion location.
431 */
Daniel Veillarda10efa82001-04-18 13:09:01 +0000432 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000433 if (table->table[key] == NULL) {
434 insert = NULL;
435 } else {
436 for (insert = table->table[key]; insert->next != NULL;
437 insert = insert->next) {
438 if ((xmlStrEqual(insert->name, name)) &&
439 (xmlStrEqual(insert->name2, name2)) &&
440 (xmlStrEqual(insert->name3, name3))) {
441 if (f)
442 f(insert->payload, insert->name);
443 insert->payload = userdata;
444 return(0);
445 }
446 }
447 if ((xmlStrEqual(insert->name, name)) &&
448 (xmlStrEqual(insert->name2, name2)) &&
449 (xmlStrEqual(insert->name3, name3))) {
450 if (f)
451 f(insert->payload, insert->name);
452 insert->payload = userdata;
453 return(0);
454 }
455 }
456
457 entry = xmlMalloc(sizeof(xmlHashEntry));
458 if (entry == NULL)
459 return(-1);
460 entry->name = xmlStrdup(name);
461 entry->name2 = xmlStrdup(name2);
462 entry->name3 = xmlStrdup(name3);
463 entry->payload = userdata;
464 entry->next = NULL;
465 table->nbElems++;
466
467
468 if (insert == NULL) {
469 table->table[key] = entry;
470 } else {
471 insert->next = entry;
472 }
473 return(0);
474}
475
476/**
477 * xmlHashLookup:
478 * @table: the hash table
479 * @name: the name of the userdata
480 * @name2: a second name of the userdata
481 * @name3: a third name of the userdata
482 *
483 * Find the userdata specified by the (name, name2, name3) tuple.
484 *
485 * Returns the a pointer to the userdata
486 */
487void *
488xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
489 const xmlChar *name2, const xmlChar *name3) {
490 unsigned long key;
491 xmlHashEntryPtr entry;
492
493 if (table == NULL)
494 return(NULL);
495 if (name == NULL)
496 return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000497 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000498 for (entry = table->table[key]; entry != NULL; entry = entry->next) {
499 if ((xmlStrEqual(entry->name, name)) &&
500 (xmlStrEqual(entry->name2, name2)) &&
501 (xmlStrEqual(entry->name3, name3)))
502 return(entry->payload);
503 }
504 return(NULL);
505}
506
507/**
508 * xmlHashScan:
509 * @table: the hash table
510 * @f: the scanner function for items in the hash
511 * @data: extra data passed to f
512 *
513 * Scan the hash table and applied f to each value.
514 */
515void
516xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
517 int i;
518 xmlHashEntryPtr iter;
519 xmlHashEntryPtr next;
520
521 if (table == NULL)
522 return;
523 if (f == NULL)
524 return;
525
526 if (table->table) {
527 for(i = 0; i < table->size; i++) {
528 iter = table->table[i];
529 while (iter) {
530 next = iter->next;
531 if (f)
532 f(iter->payload, data, iter->name);
533 iter = next;
534 }
535 }
536 }
537}
538
539/**
540 * xmlHashScan3:
541 * @table: the hash table
542 * @name: the name of the userdata or NULL
543 * @name2: a second name of the userdata or NULL
544 * @name3: a third name of the userdata or NULL
545 * @f: the scanner function for items in the hash
546 * @data: extra data passed to f
547 *
548 * Scan the hash table and applied f to each value matching
549 * (name, name2, name3) tuple. If one of the names is null,
550 * the comparison is considered to match.
551 */
552void
553xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
554 const xmlChar *name2, const xmlChar *name3,
555 xmlHashScanner f, void *data) {
556 int i;
557 xmlHashEntryPtr iter;
558 xmlHashEntryPtr next;
559
560 if (table == NULL)
561 return;
562 if (f == NULL)
563 return;
564
565 if (table->table) {
566 for(i = 0; i < table->size; i++) {
567 iter = table->table[i];
568 while (iter) {
569 next = iter->next;
570 if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
571 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
572 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3)))) {
573 f(iter->payload, data, iter->name);
574 }
575 iter = next;
576 }
577 }
578 }
579}
580
581/**
582 * xmlHashCopy:
583 * @table: the hash table
584 * @f: the copier function for items in the hash
585 *
586 * Scan the hash table and applied f to each value.
587 *
588 * Returns the new table or NULL in case of error.
589 */
590xmlHashTablePtr
591xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
592 int i;
593 xmlHashEntryPtr iter;
594 xmlHashEntryPtr next;
595 xmlHashTablePtr ret;
596
597 if (table == NULL)
598 return(NULL);
599 if (f == NULL)
600 return(NULL);
601
602 ret = xmlHashCreate(table->size);
603 if (table->table) {
604 for(i = 0; i < table->size; i++) {
605 iter = table->table[i];
606 while (iter) {
607 next = iter->next;
608 xmlHashAddEntry3(ret, iter->name, iter->name2,
609 iter->name3, f(iter->payload, iter->name));
610 iter = next;
611 }
612 }
613 }
614 ret->nbElems = table->nbElems;
615 return(ret);
616}
617
618/**
619 * xmlHashSize:
620 * @table: the hash table
621 *
622 * Returns the number of elements in the hash table or
623 * -1 in case of error
624 */
625int
626xmlHashSize(xmlHashTablePtr table) {
627 if (table == NULL)
628 return(-1);
629 return(table->nbElems);
630}
631
632/**
633 * @table: the hash table
634 * @name: the name of the userdata
635 * @f: the deallocator function for removed item (if any)
636 *
637 * Find the userdata specified by the (name, name2, name3) tuple and remove
638 * it from the hash table. Existing userdata for this tuple will be removed
639 * and freed with @f.
640 *
641 * Returns 0 if the removal succeeded and -1 in case of error or not found.
642 */
643int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000644 xmlHashDeallocator f) {
645 return(xmlHashRemoveEntry3(table, name, NULL, NULL, f));
Owen Taylor3473f882001-02-23 17:55:21 +0000646}
647
648/**
649 * @table: the hash table
650 * @name: the name of the userdata
651 * @name2: a second name of the userdata
652 * @f: the deallocator function for removed item (if any)
653 *
654 * Find the userdata specified by the (name, name2, name3) tuple and remove
655 * it from the hash table. Existing userdata for this tuple will be removed
656 * and freed with @f.
657 *
658 * Returns 0 if the removal succeeded and -1 in case of error or not found.
659 */
660int xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000661 const xmlChar *name2, xmlHashDeallocator f) {
662 return(xmlHashRemoveEntry3(table, name, name2, NULL, f));
Owen Taylor3473f882001-02-23 17:55:21 +0000663}
664
665/**
666 * @table: the hash table
667 * @name: the name of the userdata
668 * @name2: a second name of the userdata
669 * @name3: a third name of the userdata
670 * @f: the deallocator function for removed item (if any)
671 *
672 * Find the userdata specified by the (name, name2, name3) tuple and remove
673 * it from the hash table. Existing userdata for this tuple will be removed
674 * and freed with @f.
675 *
676 * Returns 0 if the removal succeeded and -1 in case of error or not found.
677 */
678int xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000679 const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
680 unsigned long key;
681 xmlHashEntryPtr entry;
682 xmlHashEntryPtr prev = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000683
Daniel Veillarda10efa82001-04-18 13:09:01 +0000684 if (table == NULL || name == NULL)
685 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000686
Daniel Veillarda10efa82001-04-18 13:09:01 +0000687 key = xmlHashComputeKey(table, name, name2, name3);
688 if (table->table[key] == NULL) {
689 return(-1);
690 } else {
691 for (entry = table->table[key]; entry != NULL; entry = entry->next) {
692 if (xmlStrEqual(entry->name, name) &&
693 xmlStrEqual(entry->name2, name2) &&
694 xmlStrEqual(entry->name3, name3)) {
695 if(f)
696 f(entry->payload, entry->name);
697 entry->payload = NULL;
698 if(entry->name)
699 xmlFree(entry->name);
700 if(entry->name2)
701 xmlFree(entry->name2);
702 if(entry->name3)
703 xmlFree(entry->name3);
704 if(prev)
705 prev->next = entry->next;
706 else
707 table->table[key] = entry->next;
708 xmlFree(entry);
709 table->nbElems--;
710 return(0);
711 }
712 prev = entry;
713 }
714 return(-1);
715 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000716}
717