VirtualBox

source: vbox/trunk/src/libs/libxml2-2.13.2/hash.c@ 107044

Last change on this file since 107044 was 105420, checked in by vboxsync, 4 months ago

libxml2-2.12.6: Applied and adjusted our libxml2 changes to 2.12.6. bugref:10730

  • Property svn:eol-style set to native
File size: 34.4 KB
Line 
1/*
2 * hash.c: hash tables
3 *
4 * Hash table with open addressing, linear probing and
5 * Robin Hood reordering.
6 *
7 * See Copyright for the status of this software.
8 */
9
10#define IN_LIBXML
11#include "libxml.h"
12
13#include <string.h>
14#include <limits.h>
15
16#include <libxml/parser.h>
17#include <libxml/hash.h>
18#include <libxml/dict.h>
19#include <libxml/xmlmemory.h>
20#include <libxml/xmlstring.h>
21
22#include "private/dict.h"
23
24#ifndef SIZE_MAX
25 #define SIZE_MAX ((size_t) -1)
26#endif
27
28#define MAX_FILL_NUM 7
29#define MAX_FILL_DENOM 8
30#define MIN_HASH_SIZE 8
31#define MAX_HASH_SIZE (1u << 31)
32
33/*
34 * A single entry in the hash table
35 */
36typedef struct {
37 unsigned hashValue; /* 0 means unoccupied, occupied entries have the
38 * MAX_HASH_SIZE bit set to 1 */
39 xmlChar *key;
40 xmlChar *key2; /* TODO: Don't allocate possibly empty keys */
41 xmlChar *key3;
42 void *payload;
43} xmlHashEntry;
44
45/*
46 * The entire hash table
47 */
48struct _xmlHashTable {
49 xmlHashEntry *table;
50 unsigned size; /* power of two */
51 unsigned nbElems;
52 xmlDictPtr dict;
53 unsigned randomSeed;
54};
55
56static int
57xmlHashGrow(xmlHashTablePtr hash, unsigned size);
58
59ATTRIBUTE_NO_SANITIZE_INTEGER
60static unsigned
61xmlHashValue(unsigned seed, const xmlChar *key, const xmlChar *key2,
62 const xmlChar *key3, size_t *lengths) {
63 unsigned h1, h2;
64 size_t i;
65
66 HASH_INIT(h1, h2, seed);
67
68 for (i = 0; key[i] != 0; i++) {
69 HASH_UPDATE(h1, h2, key[i]);
70 }
71 if (lengths)
72 lengths[0] = i;
73
74 HASH_UPDATE(h1, h2, 0);
75
76 if (key2 != NULL) {
77 for (i = 0; key2[i] != 0; i++) {
78 HASH_UPDATE(h1, h2, key2[i]);
79 }
80 if (lengths)
81 lengths[1] = i;
82 }
83
84 HASH_UPDATE(h1, h2, 0);
85
86 if (key3 != NULL) {
87 for (i = 0; key3[i] != 0; i++) {
88 HASH_UPDATE(h1, h2, key3[i]);
89 }
90 if (lengths)
91 lengths[2] = i;
92 }
93
94 HASH_FINISH(h1, h2);
95
96 return(h2);
97}
98
99ATTRIBUTE_NO_SANITIZE_INTEGER
100static unsigned
101xmlHashQNameValue(unsigned seed,
102 const xmlChar *prefix, const xmlChar *name,
103 const xmlChar *prefix2, const xmlChar *name2,
104 const xmlChar *prefix3, const xmlChar *name3) {
105 unsigned h1, h2, ch;
106
107 HASH_INIT(h1, h2, seed);
108
109 if (prefix != NULL) {
110 while ((ch = *prefix++) != 0) {
111 HASH_UPDATE(h1, h2, ch);
112 }
113 HASH_UPDATE(h1, h2, ':');
114 }
115 if (name != NULL) {
116 while ((ch = *name++) != 0) {
117 HASH_UPDATE(h1, h2, ch);
118 }
119 }
120 HASH_UPDATE(h1, h2, 0);
121 if (prefix2 != NULL) {
122 while ((ch = *prefix2++) != 0) {
123 HASH_UPDATE(h1, h2, ch);
124 }
125 HASH_UPDATE(h1, h2, ':');
126 }
127 if (name2 != NULL) {
128 while ((ch = *name2++) != 0) {
129 HASH_UPDATE(h1, h2, ch);
130 }
131 }
132 HASH_UPDATE(h1, h2, 0);
133 if (prefix3 != NULL) {
134 while ((ch = *prefix3++) != 0) {
135 HASH_UPDATE(h1, h2, ch);
136 }
137 HASH_UPDATE(h1, h2, ':');
138 }
139 if (name3 != NULL) {
140 while ((ch = *name3++) != 0) {
141 HASH_UPDATE(h1, h2, ch);
142 }
143 }
144
145 HASH_FINISH(h1, h2);
146
147 return(h2);
148}
149
150/**
151 * xmlHashCreate:
152 * @size: initial size of the hash table
153 *
154 * Create a new hash table. Set size to zero if the number of entries
155 * can't be estimated.
156 *
157 * Returns the newly created object, or NULL if a memory allocation failed.
158 */
159xmlHashTablePtr
160xmlHashCreate(int size) {
161 xmlHashTablePtr hash;
162
163 xmlInitParser();
164
165 hash = xmlMalloc(sizeof(*hash));
166 if (hash == NULL)
167 return(NULL);
168 hash->dict = NULL;
169 hash->size = 0;
170 hash->table = NULL;
171 hash->nbElems = 0;
172 hash->randomSeed = xmlRandom();
173#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
174 hash->randomSeed = 0;
175#endif
176
177 /*
178 * Unless a larger size is passed, the backing table is created
179 * lazily with MIN_HASH_SIZE capacity. In practice, there are many
180 * hash tables which are never filled.
181 */
182 if (size > MIN_HASH_SIZE) {
183 unsigned newSize = MIN_HASH_SIZE * 2;
184
185 while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE))
186 newSize *= 2;
187
188 if (xmlHashGrow(hash, newSize) != 0) {
189 xmlFree(hash);
190 return(NULL);
191 }
192 }
193
194 return(hash);
195}
196
197/**
198 * xmlHashCreateDict:
199 * @size: the size of the hash table
200 * @dict: a dictionary to use for the hash
201 *
202 * Create a new hash table backed by a dictionary. This can reduce
203 * resource usage considerably if most keys passed to API functions
204 * originate from this dictionary.
205 *
206 * Returns the newly created object, or NULL if a memory allocation failed.
207 */
208xmlHashTablePtr
209xmlHashCreateDict(int size, xmlDictPtr dict) {
210 xmlHashTablePtr hash;
211
212 hash = xmlHashCreate(size);
213 if (hash != NULL) {
214 hash->dict = dict;
215 xmlDictReference(dict);
216 }
217 return(hash);
218}
219
220/**
221 * xmlHashFree:
222 * @hash: hash table
223 * @dealloc: deallocator function or NULL
224 *
225 * Free the hash and its contents. The payload is deallocated with
226 * @dealloc if provided.
227 */
228void
229xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) {
230 if (hash == NULL)
231 return;
232
233 if (hash->table) {
234 const xmlHashEntry *end = &hash->table[hash->size];
235 const xmlHashEntry *entry;
236
237 for (entry = hash->table; entry < end; entry++) {
238 if (entry->hashValue == 0)
239 continue;
240 if ((dealloc != NULL) && (entry->payload != NULL))
241 dealloc(entry->payload, entry->key);
242 if (hash->dict == NULL) {
243 if (entry->key)
244 xmlFree(entry->key);
245 if (entry->key2)
246 xmlFree(entry->key2);
247 if (entry->key3)
248 xmlFree(entry->key3);
249 }
250 }
251
252 xmlFree(hash->table);
253 }
254
255 if (hash->dict)
256 xmlDictFree(hash->dict);
257
258 xmlFree(hash);
259}
260
261/**
262 * xmlFastStrEqual:
263 * @s1: string
264 * @s2: string
265 *
266 * Compare two strings for equality, allowing NULL values.
267 */
268static int
269xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) {
270 if (s1 == NULL)
271 return(s2 == NULL);
272 else
273 return((s2 != NULL) &&
274 (strcmp((const char *) s1, (const char *) s2) == 0));
275}
276
277/**
278 * xmlHashFindEntry:
279 * @hash: hash table, non-NULL, size > 0
280 * @key: first string key, non-NULL
281 * @key2: second string key
282 * @key3: third string key
283 * @hashValue: valid hash value of keys
284 * @pfound: result of search
285 *
286 * Try to find a matching hash table entry. If an entry was found, set
287 * @found to 1 and return the entry. Otherwise, set @found to 0 and return
288 * the location where a new entry should be inserted.
289 */
290ATTRIBUTE_NO_SANITIZE_INTEGER
291static xmlHashEntry *
292xmlHashFindEntry(const xmlHashTable *hash, const xmlChar *key,
293 const xmlChar *key2, const xmlChar *key3,
294 unsigned hashValue, int *pfound) {
295 xmlHashEntry *entry;
296 unsigned mask, pos, displ;
297 int found = 0;
298
299 mask = hash->size - 1;
300 pos = hashValue & mask;
301 entry = &hash->table[pos];
302
303 if (entry->hashValue != 0) {
304 /*
305 * Robin hood hashing: abort if the displacement of the entry
306 * is smaller than the displacement of the key we look for.
307 * This also stops at the correct position when inserting.
308 */
309 displ = 0;
310 hashValue |= MAX_HASH_SIZE;
311
312 do {
313 if (entry->hashValue == hashValue) {
314 if (hash->dict) {
315 if ((entry->key == key) &&
316 (entry->key2 == key2) &&
317 (entry->key3 == key3)) {
318 found = 1;
319 break;
320 }
321 }
322 if ((strcmp((const char *) entry->key,
323 (const char *) key) == 0) &&
324 (xmlFastStrEqual(entry->key2, key2)) &&
325 (xmlFastStrEqual(entry->key3, key3))) {
326 found = 1;
327 break;
328 }
329 }
330
331 displ++;
332 pos++;
333 entry++;
334 if ((pos & mask) == 0)
335 entry = hash->table;
336 } while ((entry->hashValue != 0) &&
337 (((pos - entry->hashValue) & mask) >= displ));
338 }
339
340 *pfound = found;
341 return(entry);
342}
343
344/**
345 * xmlHashGrow:
346 * @hash: hash table
347 * @size: new size of the hash table
348 *
349 * Resize the hash table.
350 *
351 * Returns 0 in case of success, -1 if a memory allocation failed.
352 */
353static int
354xmlHashGrow(xmlHashTablePtr hash, unsigned size) {
355 const xmlHashEntry *oldentry, *oldend, *end;
356 xmlHashEntry *table;
357 unsigned oldsize, i;
358
359 /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
360 if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
361 return(-1);
362 table = xmlMalloc(size * sizeof(table[0]));
363 if (table == NULL)
364 return(-1);
365 memset(table, 0, size * sizeof(table[0]));
366
367 oldsize = hash->size;
368 if (oldsize == 0)
369 goto done;
370
371 oldend = &hash->table[oldsize];
372 end = &table[size];
373
374 /*
375 * Robin Hood sorting order is maintained if we
376 *
377 * - compute hash indices with modulo
378 * - resize by an integer factor
379 * - start to copy from the beginning of a probe sequence
380 */
381 oldentry = hash->table;
382 while (oldentry->hashValue != 0) {
383 if (++oldentry >= oldend)
384 oldentry = hash->table;
385 }
386
387 for (i = 0; i < oldsize; i++) {
388 if (oldentry->hashValue != 0) {
389 xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)];
390
391 while (entry->hashValue != 0) {
392 if (++entry >= end)
393 entry = table;
394 }
395 *entry = *oldentry;
396 }
397
398 if (++oldentry >= oldend)
399 oldentry = hash->table;
400 }
401
402 xmlFree(hash->table);
403
404done:
405 hash->table = table;
406 hash->size = size;
407
408 return(0);
409}
410
411/**
412 * xmlHashUpdateInternal:
413 * @hash: hash table
414 * @key: first string key
415 * @key2: second string key
416 * @key3: third string key
417 * @payload: pointer to the payload
418 * @dealloc: deallocator function for replaced item or NULL
419 * @update: whether existing entries should be updated
420 *
421 * Internal function to add or update hash entries.
422 */
423ATTRIBUTE_NO_SANITIZE_INTEGER
424static int
425xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key,
426 const xmlChar *key2, const xmlChar *key3,
427 void *payload, xmlHashDeallocator dealloc, int update) {
428 xmlChar *copy, *copy2, *copy3;
429 xmlHashEntry *entry = NULL;
430 size_t lengths[3];
431 unsigned hashValue;
432 int found = 0;
433
434 if ((hash == NULL) || (key == NULL))
435 return(-1);
436
437 /*
438 * Check for an existing entry
439 */
440 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths);
441 if (hash->size > 0)
442 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
443 if (found) {
444 if (update) {
445 if (dealloc)
446 dealloc(entry->payload, entry->key);
447 entry->payload = payload;
448 }
449
450 return(0);
451 }
452
453 /*
454 * Grow the hash table if needed
455 */
456 if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
457 unsigned newSize, mask, displ, pos;
458
459 if (hash->size == 0) {
460 newSize = MIN_HASH_SIZE;
461 } else {
462 /* This guarantees that nbElems < INT_MAX */
463 if (hash->size >= MAX_HASH_SIZE)
464 return(-1);
465 newSize = hash->size * 2;
466 }
467 if (xmlHashGrow(hash, newSize) != 0)
468 return(-1);
469
470 /*
471 * Find new entry
472 */
473 mask = hash->size - 1;
474 displ = 0;
475 pos = hashValue & mask;
476 entry = &hash->table[pos];
477
478 if (entry->hashValue != 0) {
479 do {
480 displ++;
481 pos++;
482 entry++;
483 if ((pos & mask) == 0)
484 entry = hash->table;
485 } while ((entry->hashValue != 0) &&
486 ((pos - entry->hashValue) & mask) >= displ);
487 }
488 }
489
490 /*
491 * Copy keys
492 */
493 if (hash->dict != NULL) {
494 if (xmlDictOwns(hash->dict, key)) {
495 copy = (xmlChar *) key;
496 } else {
497 copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
498 if (copy == NULL)
499 return(-1);
500 }
501
502 if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
503 copy2 = (xmlChar *) key2;
504 } else {
505 copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
506 if (copy2 == NULL)
507 return(-1);
508 }
509 if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
510 copy3 = (xmlChar *) key3;
511 } else {
512 copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
513 if (copy3 == NULL)
514 return(-1);
515 }
516 } else {
517 copy = xmlMalloc(lengths[0] + 1);
518 if (copy == NULL)
519 return(-1);
520 memcpy(copy, key, lengths[0] + 1);
521
522 if (key2 != NULL) {
523 copy2 = xmlMalloc(lengths[1] + 1);
524 if (copy2 == NULL) {
525 xmlFree(copy);
526 return(-1);
527 }
528 memcpy(copy2, key2, lengths[1] + 1);
529 } else {
530 copy2 = NULL;
531 }
532
533 if (key3 != NULL) {
534 copy3 = xmlMalloc(lengths[2] + 1);
535 if (copy3 == NULL) {
536 xmlFree(copy);
537 xmlFree(copy2);
538 return(-1);
539 }
540 memcpy(copy3, key3, lengths[2] + 1);
541 } else {
542 copy3 = NULL;
543 }
544 }
545
546 /*
547 * Shift the remainder of the probe sequence to the right
548 */
549 if (entry->hashValue != 0) {
550 const xmlHashEntry *end = &hash->table[hash->size];
551 const xmlHashEntry *cur = entry;
552
553 do {
554 cur++;
555 if (cur >= end)
556 cur = hash->table;
557 } while (cur->hashValue != 0);
558
559 if (cur < entry) {
560 /*
561 * If we traversed the end of the buffer, handle the part
562 * at the start of the buffer.
563 */
564 memmove(&hash->table[1], hash->table,
565 (char *) cur - (char *) hash->table);
566 cur = end - 1;
567 hash->table[0] = *cur;
568 }
569
570 memmove(&entry[1], entry, (char *) cur - (char *) entry);
571 }
572
573 /*
574 * Populate entry
575 */
576 entry->key = copy;
577 entry->key2 = copy2;
578 entry->key3 = copy3;
579 entry->payload = payload;
580 /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
581 entry->hashValue = hashValue | MAX_HASH_SIZE;
582
583 hash->nbElems++;
584
585 return(1);
586}
587
588/**
589 * xmlHashDefaultDeallocator:
590 * @entry: hash table entry
591 * @key: the entry's string key
592 *
593 * Free a hash table entry with xmlFree.
594 */
595void
596xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
597 xmlFree(entry);
598}
599
600/**
601 * xmlHashAdd:
602 * @hash: hash table
603 * @key: string key
604 * @payload: pointer to the payload
605 *
606 * Add a hash table entry. If an entry with this key already exists,
607 * payload will not be updated and 0 is returned. This return value
608 * can't be distinguished from out-of-memory errors, so this function
609 * should be used with care.
610 *
611 * Available since 2.13.0.
612 *
613 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
614 */
615int
616xmlHashAdd(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
617 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
618}
619
620/**
621 * xmlHashAdd2:
622 * @hash: hash table
623 * @key: first string key
624 * @key2: second string key
625 * @payload: pointer to the payload
626 *
627 * Add a hash table entry with two strings as key.
628 *
629 * See xmlHashAdd.
630 *
631 * Available since 2.13.0.
632 *
633 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
634 */
635int
636xmlHashAdd2(xmlHashTablePtr hash, const xmlChar *key,
637 const xmlChar *key2, void *payload) {
638 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
639}
640
641/**
642 * xmlHashAdd3:
643 * @hash: hash table
644 * @key: first string key
645 * @key2: second string key
646 * @key3: third string key
647 * @payload: pointer to the payload
648 *
649 * Add a hash table entry with three strings as key.
650 *
651 * See xmlHashAdd.
652 *
653 * Available since 2.13.0.
654 *
655 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
656 */
657int
658xmlHashAdd3(xmlHashTablePtr hash, const xmlChar *key,
659 const xmlChar *key2, const xmlChar *key3,
660 void *payload) {
661 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
662}
663
664/**
665 * xmlHashAddEntry:
666 * @hash: hash table
667 * @key: string key
668 * @payload: pointer to the payload
669 *
670 * Add a hash table entry. If an entry with this key already exists,
671 * payload will not be updated and -1 is returned. This return value
672 * can't be distinguished from out-of-memory errors, so this function
673 * should be used with care.
674 *
675 * NOTE: This function doesn't allow to distinguish malloc failures from
676 * existing entries. Use xmlHashAdd instead.
677 *
678 * Returns 0 on success and -1 in case of error.
679 */
680int
681xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
682 int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0);
683
684 if (res == 0)
685 res = -1;
686 else if (res == 1)
687 res = 0;
688
689 return(res);
690}
691
692/**
693 * xmlHashAddEntry2:
694 * @hash: hash table
695 * @key: first string key
696 * @key2: second string key
697 * @payload: pointer to the payload
698 *
699 * Add a hash table entry with two strings as key.
700 *
701 * See xmlHashAddEntry.
702 *
703 * Returns 0 on success and -1 in case of error.
704 */
705int
706xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
707 const xmlChar *key2, void *payload) {
708 int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0);
709
710 if (res == 0)
711 res = -1;
712 else if (res == 1)
713 res = 0;
714
715 return(res);
716}
717
718/**
719 * xmlHashAddEntry3:
720 * @hash: hash table
721 * @key: first string key
722 * @key2: second string key
723 * @key3: third string key
724 * @payload: pointer to the payload
725 *
726 * Add a hash table entry with three strings as key.
727 *
728 * See xmlHashAddEntry.
729 *
730 * Returns 0 on success and -1 in case of error.
731 */
732int
733xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
734 const xmlChar *key2, const xmlChar *key3,
735 void *payload) {
736 int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0);
737
738 if (res == 0)
739 res = -1;
740 else if (res == 1)
741 res = 0;
742
743 return(res);
744}
745
746/**
747 * xmlHashUpdateEntry:
748 * @hash: hash table
749 * @key: string key
750 * @payload: pointer to the payload
751 * @dealloc: deallocator function for replaced item or NULL
752 *
753 * Add a hash table entry. If an entry with this key already exists,
754 * the old payload will be freed and updated with the new value.
755 *
756 * Returns 0 in case of success, -1 if a memory allocation failed.
757 */
758int
759xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
760 void *payload, xmlHashDeallocator dealloc) {
761 int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
762 dealloc, 1);
763
764 if (res == 1)
765 res = 0;
766
767 return(res);
768}
769
770/**
771 * xmlHashUpdateEntry2:
772 * @hash: hash table
773 * @key: first string key
774 * @key2: second string key
775 * @payload: pointer to the payload
776 * @dealloc: deallocator function for replaced item or NULL
777 *
778 * Add a hash table entry with two strings as key.
779 *
780 * See xmlHashUpdateEntry.
781 *
782 * Returns 0 on success and -1 in case of error.
783 */
784int
785xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
786 const xmlChar *key2, void *payload,
787 xmlHashDeallocator dealloc) {
788 int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload,
789 dealloc, 1);
790
791 if (res == 1)
792 res = 0;
793
794 return(res);
795}
796
797/**
798 * xmlHashUpdateEntry3:
799 * @hash: hash table
800 * @key: first string key
801 * @key2: second string key
802 * @key3: third string key
803 * @payload: pointer to the payload
804 * @dealloc: deallocator function for replaced item or NULL
805 *
806 * Add a hash table entry with three strings as key.
807 *
808 * See xmlHashUpdateEntry.
809 *
810 * Returns 0 on success and -1 in case of error.
811 */
812int
813xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
814 const xmlChar *key2, const xmlChar *key3,
815 void *payload, xmlHashDeallocator dealloc) {
816 int res = xmlHashUpdateInternal(hash, key, key2, key3, payload,
817 dealloc, 1);
818
819 if (res == 1)
820 res = 0;
821
822 return(res);
823}
824
825/**
826 * xmlHashLookup:
827 * @hash: hash table
828 * @key: string key
829 *
830 * Find the entry specified by @key.
831 *
832 * Returns a pointer to the payload or NULL if no entry was found.
833 */
834void *
835xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
836 return(xmlHashLookup3(hash, key, NULL, NULL));
837}
838
839/**
840 * xmlHashLookup2:
841 * @hash: hash table
842 * @key: first string key
843 * @key2: second string key
844 *
845 * Find the payload specified by the (@key, @key2) tuple.
846 *
847 * Returns a pointer to the payload or NULL if no entry was found.
848 */
849void *
850xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
851 const xmlChar *key2) {
852 return(xmlHashLookup3(hash, key, key2, NULL));
853}
854
855/**
856 * xmlHashQLookup:
857 * @hash: hash table
858 * @prefix: prefix of the string key
859 * @name: local name of the string key
860 *
861 * Find the payload specified by the QName @prefix:@name or @name.
862 *
863 * Returns a pointer to the payload or NULL if no entry was found.
864 */
865void *
866xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
867 const xmlChar *name) {
868 return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
869}
870
871/**
872 * xmlHashQLookup2:
873 * @hash: hash table
874 * @prefix: first prefix
875 * @name: first local name
876 * @prefix2: second prefix
877 * @name2: second local name
878 *
879 * Find the payload specified by the QNames tuple.
880 *
881 * Returns a pointer to the payload or NULL if no entry was found.
882 */
883void *
884xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
885 const xmlChar *name, const xmlChar *prefix2,
886 const xmlChar *name2) {
887 return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
888}
889
890/**
891 * xmlHashLookup3:
892 * @hash: hash table
893 * @key: first string key
894 * @key2: second string key
895 * @key3: third string key
896 *
897 * Find the payload specified by the (@key, @key2, @key3) tuple.
898 *
899 * Returns a pointer to the payload or NULL if no entry was found.
900 */
901void *
902xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
903 const xmlChar *key2, const xmlChar *key3) {
904 const xmlHashEntry *entry;
905 unsigned hashValue;
906 int found;
907
908 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
909 return(NULL);
910 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
911 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
912 if (found)
913 return(entry->payload);
914 return(NULL);
915}
916
917/**
918 * xmlHashQLookup3:
919 * @hash: hash table
920 * @prefix: first prefix
921 * @name: first local name
922 * @prefix2: second prefix
923 * @name2: second local name
924 * @prefix3: third prefix
925 * @name3: third local name
926 *
927 * Find the payload specified by the QNames tuple.
928 *
929 * Returns a pointer to the payload or NULL if no entry was found.
930 */
931ATTRIBUTE_NO_SANITIZE_INTEGER
932void *
933xmlHashQLookup3(xmlHashTablePtr hash,
934 const xmlChar *prefix, const xmlChar *name,
935 const xmlChar *prefix2, const xmlChar *name2,
936 const xmlChar *prefix3, const xmlChar *name3) {
937 const xmlHashEntry *entry;
938 unsigned hashValue, mask, pos, displ;
939
940 if ((hash == NULL) || (hash->size == 0) || (name == NULL))
941 return(NULL);
942
943 hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
944 name2, prefix3, name3);
945 mask = hash->size - 1;
946 pos = hashValue & mask;
947 entry = &hash->table[pos];
948
949 if (entry->hashValue != 0) {
950 displ = 0;
951 hashValue |= MAX_HASH_SIZE;
952
953 do {
954 if ((hashValue == entry->hashValue) &&
955 (xmlStrQEqual(prefix, name, entry->key)) &&
956 (xmlStrQEqual(prefix2, name2, entry->key2)) &&
957 (xmlStrQEqual(prefix3, name3, entry->key3)))
958 return(entry->payload);
959
960 displ++;
961 pos++;
962 entry++;
963 if ((pos & mask) == 0)
964 entry = hash->table;
965 } while ((entry->hashValue != 0) &&
966 (((pos - entry->hashValue) & mask) >= displ));
967 }
968
969 return(NULL);
970}
971
972typedef struct {
973 xmlHashScanner scan;
974 void *data;
975} stubData;
976
977static void
978stubHashScannerFull(void *payload, void *data, const xmlChar *key,
979 const xmlChar *key2 ATTRIBUTE_UNUSED,
980 const xmlChar *key3 ATTRIBUTE_UNUSED) {
981 stubData *sdata = (stubData *) data;
982 sdata->scan(payload, sdata->data, key);
983}
984
985/**
986 * xmlHashScan:
987 * @hash: hash table
988 * @scan: scanner function for items in the hash
989 * @data: extra data passed to @scan
990 *
991 * Scan the hash @table and apply @scan to each value.
992 */
993void
994xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
995 stubData sdata;
996 sdata.data = data;
997 sdata.scan = scan;
998 xmlHashScanFull(hash, stubHashScannerFull, &sdata);
999}
1000
1001/**
1002 * xmlHashScanFull:
1003 * @hash: hash table
1004 * @scan: scanner function for items in the hash
1005 * @data: extra data passed to @scan
1006 *
1007 * Scan the hash @table and apply @scan to each value.
1008 */
1009void
1010xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
1011 const xmlHashEntry *entry, *end;
1012 xmlHashEntry old;
1013 unsigned i;
1014
1015 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1016 return;
1017
1018 /*
1019 * We must handle the case that a scanned entry is removed when executing
1020 * the callback (xmlCleanSpecialAttr and possibly other places).
1021 *
1022 * Find the start of a probe sequence to avoid scanning entries twice if
1023 * a deletion happens.
1024 */
1025 entry = hash->table;
1026 end = &hash->table[hash->size];
1027 while (entry->hashValue != 0) {
1028 if (++entry >= end)
1029 entry = hash->table;
1030 }
1031
1032 for (i = 0; i < hash->size; i++) {
1033 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1034 /*
1035 * Make sure to rescan after a possible deletion.
1036 */
1037 do {
1038 old = *entry;
1039 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1040 } while ((entry->hashValue != 0) &&
1041 (entry->payload != NULL) &&
1042 ((entry->key != old.key) ||
1043 (entry->key2 != old.key2) ||
1044 (entry->key3 != old.key3)));
1045 }
1046 if (++entry >= end)
1047 entry = hash->table;
1048 }
1049}
1050
1051/**
1052 * xmlHashScan3:
1053 * @hash: hash table
1054 * @key: first string key or NULL
1055 * @key2: second string key or NULL
1056 * @key3: third string key or NULL
1057 * @scan: scanner function for items in the hash
1058 * @data: extra data passed to @scan
1059 *
1060 * Scan the hash @table and apply @scan to each value matching
1061 * (@key, @key2, @key3) tuple. If one of the keys is null,
1062 * the comparison is considered to match.
1063 */
1064void
1065xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
1066 const xmlChar *key2, const xmlChar *key3,
1067 xmlHashScanner scan, void *data) {
1068 stubData sdata;
1069 sdata.data = data;
1070 sdata.scan = scan;
1071 xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
1072}
1073
1074/**
1075 * xmlHashScanFull3:
1076 * @hash: hash table
1077 * @key: first string key or NULL
1078 * @key2: second string key or NULL
1079 * @key3: third string key or NULL
1080 * @scan: scanner function for items in the hash
1081 * @data: extra data passed to @scan
1082 *
1083 * Scan the hash @table and apply @scan to each value matching
1084 * (@key, @key2, @key3) tuple. If one of the keys is null,
1085 * the comparison is considered to match.
1086 */
1087void
1088xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
1089 const xmlChar *key2, const xmlChar *key3,
1090 xmlHashScannerFull scan, void *data) {
1091 const xmlHashEntry *entry, *end;
1092 xmlHashEntry old;
1093 unsigned i;
1094
1095 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1096 return;
1097
1098 /*
1099 * We must handle the case that a scanned entry is removed when executing
1100 * the callback (xmlCleanSpecialAttr and possibly other places).
1101 *
1102 * Find the start of a probe sequence to avoid scanning entries twice if
1103 * a deletion happens.
1104 */
1105 entry = hash->table;
1106 end = &hash->table[hash->size];
1107 while (entry->hashValue != 0) {
1108 if (++entry >= end)
1109 entry = hash->table;
1110 }
1111
1112 for (i = 0; i < hash->size; i++) {
1113 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1114 /*
1115 * Make sure to rescan after a possible deletion.
1116 */
1117 do {
1118 if (((key != NULL) && (strcmp((const char *) key,
1119 (const char *) entry->key) != 0)) ||
1120 ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
1121 ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
1122 break;
1123 old = *entry;
1124 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1125 } while ((entry->hashValue != 0) &&
1126 (entry->payload != NULL) &&
1127 ((entry->key != old.key) ||
1128 (entry->key2 != old.key2) ||
1129 (entry->key3 != old.key3)));
1130 }
1131 if (++entry >= end)
1132 entry = hash->table;
1133 }
1134}
1135
1136/*
1137 * xmlHashCopySafe:
1138 * @hash: hash table
1139 * @copyFunc: copier function for items in the hash
1140 * @deallocFunc: deallocation function in case of errors
1141 *
1142 * Copy the hash table using @copyFunc to copy payloads.
1143 *
1144 * Available since 2.13.0.
1145 *
1146 * Returns the new table or NULL if a memory allocation failed.
1147 */
1148xmlHashTablePtr
1149xmlHashCopySafe(xmlHashTablePtr hash, xmlHashCopier copyFunc,
1150 xmlHashDeallocator deallocFunc) {
1151 const xmlHashEntry *entry, *end;
1152 xmlHashTablePtr ret;
1153
1154 if ((hash == NULL) || (copyFunc == NULL))
1155 return(NULL);
1156
1157 ret = xmlHashCreate(hash->size);
1158 if (ret == NULL)
1159 return(NULL);
1160
1161 if (hash->size == 0)
1162 return(ret);
1163
1164 end = &hash->table[hash->size];
1165
1166 for (entry = hash->table; entry < end; entry++) {
1167 if (entry->hashValue != 0) {
1168 void *copy;
1169
1170 copy = copyFunc(entry->payload, entry->key);
1171 if (copy == NULL)
1172 goto error;
1173 if (xmlHashAdd3(ret, entry->key, entry->key2, entry->key3,
1174 copy) <= 0) {
1175 if (deallocFunc != NULL)
1176 deallocFunc(copy, entry->key);
1177 goto error;
1178 }
1179 }
1180 }
1181
1182 return(ret);
1183
1184error:
1185 xmlHashFree(ret, deallocFunc);
1186 return(NULL);
1187}
1188
1189/*
1190 * xmlHashCopy:
1191 * @hash: hash table
1192 * @copy: copier function for items in the hash
1193 *
1194 * DEPRECATED: Leaks memory in error case.
1195 *
1196 * Copy the hash table using @copy to copy payloads.
1197 *
1198 * Returns the new table or NULL if a memory allocation failed.
1199 */
1200xmlHashTablePtr
1201xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
1202 return(xmlHashCopySafe(hash, copy, NULL));
1203}
1204
1205/**
1206 * xmlHashSize:
1207 * @hash: hash table
1208 *
1209 * Query the number of elements in the hash table.
1210 *
1211 * Returns the number of elements in the hash table or
1212 * -1 in case of error.
1213 */
1214int
1215xmlHashSize(xmlHashTablePtr hash) {
1216 if (hash == NULL)
1217 return(-1);
1218 return(hash->nbElems);
1219}
1220
1221/**
1222 * xmlHashRemoveEntry:
1223 * @hash: hash table
1224 * @key: string key
1225 * @dealloc: deallocator function for removed item or NULL
1226 *
1227 * Find the entry specified by the @key and remove it from the hash table.
1228 * Payload will be freed with @dealloc.
1229 *
1230 * Returns 0 on success and -1 if no entry was found.
1231 */
1232int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1233 xmlHashDeallocator dealloc) {
1234 return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1235}
1236
1237/**
1238 * xmlHashRemoveEntry2:
1239 * @hash: hash table
1240 * @key: first string key
1241 * @key2: second string key
1242 * @dealloc: deallocator function for removed item or NULL
1243 *
1244 * Remove an entry with two strings as key.
1245 *
1246 * See xmlHashRemoveEntry.
1247 *
1248 * Returns 0 on success and -1 in case of error.
1249 */
1250int
1251xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1252 const xmlChar *key2, xmlHashDeallocator dealloc) {
1253 return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1254}
1255
1256/**
1257 * xmlHashRemoveEntry3:
1258 * @hash: hash table
1259 * @key: first string key
1260 * @key2: second string key
1261 * @key3: third string key
1262 * @dealloc: deallocator function for removed item or NULL
1263 *
1264 * Remove an entry with three strings as key.
1265 *
1266 * See xmlHashRemoveEntry.
1267 *
1268 * Returns 0 on success and -1 in case of error.
1269 */
1270ATTRIBUTE_NO_SANITIZE_INTEGER
1271int
1272xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1273 const xmlChar *key2, const xmlChar *key3,
1274 xmlHashDeallocator dealloc) {
1275 xmlHashEntry *entry, *cur, *next;
1276 unsigned hashValue, mask, pos, nextpos;
1277 int found;
1278
1279 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1280 return(-1);
1281
1282 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1283 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1284 if (!found)
1285 return(-1);
1286
1287 if ((dealloc != NULL) && (entry->payload != NULL))
1288 dealloc(entry->payload, entry->key);
1289 if (hash->dict == NULL) {
1290 if (entry->key)
1291 xmlFree(entry->key);
1292 if (entry->key2)
1293 xmlFree(entry->key2);
1294 if (entry->key3)
1295 xmlFree(entry->key3);
1296 }
1297
1298 /*
1299 * Find end of probe sequence. Entries at their initial probe
1300 * position start a new sequence.
1301 */
1302 mask = hash->size - 1;
1303 pos = entry - hash->table;
1304 cur = entry;
1305
1306 while (1) {
1307 nextpos = pos + 1;
1308 next = cur + 1;
1309 if ((nextpos & mask) == 0)
1310 next = hash->table;
1311
1312 if ((next->hashValue == 0) ||
1313 (((next->hashValue - nextpos) & mask) == 0))
1314 break;
1315
1316 cur = next;
1317 pos = nextpos;
1318 }
1319
1320 /*
1321 * Backward shift
1322 */
1323 next = entry + 1;
1324
1325 if (cur < entry) {
1326 xmlHashEntry *end = &hash->table[hash->size];
1327
1328 memmove(entry, next, (char *) end - (char *) next);
1329 entry = hash->table;
1330 end[-1] = *entry;
1331 next = entry + 1;
1332 }
1333
1334 memmove(entry, next, (char *) cur - (char *) entry);
1335
1336 /*
1337 * Update entry
1338 */
1339 cur->hashValue = 0;
1340
1341 hash->nbElems--;
1342
1343 return(0);
1344}
1345
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette