VirtualBox

source: vbox/trunk/src/libs/libxml2-2.12.6/hash.c

Last change on this file was 104106, checked in by vboxsync, 8 weeks ago

libxml2-2.9.14: Applied and adjusted our libxml2 changes to 2.9.14. bugref:10640

  • Property svn:eol-style set to native
File size: 31.6 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 return(0);
449 } else {
450 /*
451 * xmlHashAddEntry found an existing entry.
452 *
453 * TODO: We should return a different error code here to
454 * distinguish from malloc failures.
455 */
456 return(-1);
457 }
458 }
459
460 /*
461 * Grow the hash table if needed
462 */
463 if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
464 unsigned newSize, mask, displ, pos;
465
466 if (hash->size == 0) {
467 newSize = MIN_HASH_SIZE;
468 } else {
469 /* This guarantees that nbElems < INT_MAX */
470 if (hash->size >= MAX_HASH_SIZE)
471 return(-1);
472 newSize = hash->size * 2;
473 }
474 if (xmlHashGrow(hash, newSize) != 0)
475 return(-1);
476
477 /*
478 * Find new entry
479 */
480 mask = hash->size - 1;
481 displ = 0;
482 pos = hashValue & mask;
483 entry = &hash->table[pos];
484
485 if (entry->hashValue != 0) {
486 do {
487 displ++;
488 pos++;
489 entry++;
490 if ((pos & mask) == 0)
491 entry = hash->table;
492 } while ((entry->hashValue != 0) &&
493 ((pos - entry->hashValue) & mask) >= displ);
494 }
495 }
496
497 /*
498 * Copy keys
499 */
500 if (hash->dict != NULL) {
501 if (xmlDictOwns(hash->dict, key)) {
502 copy = (xmlChar *) key;
503 } else {
504 copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
505 if (copy == NULL)
506 return(-1);
507 }
508
509 if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
510 copy2 = (xmlChar *) key2;
511 } else {
512 copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
513 if (copy2 == NULL)
514 return(-1);
515 }
516 if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
517 copy3 = (xmlChar *) key3;
518 } else {
519 copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
520 if (copy3 == NULL)
521 return(-1);
522 }
523 } else {
524 copy = xmlMalloc(lengths[0] + 1);
525 if (copy == NULL)
526 return(-1);
527 memcpy(copy, key, lengths[0] + 1);
528
529 if (key2 != NULL) {
530 copy2 = xmlMalloc(lengths[1] + 1);
531 if (copy2 == NULL) {
532 xmlFree(copy);
533 return(-1);
534 }
535 memcpy(copy2, key2, lengths[1] + 1);
536 } else {
537 copy2 = NULL;
538 }
539
540 if (key3 != NULL) {
541 copy3 = xmlMalloc(lengths[2] + 1);
542 if (copy3 == NULL) {
543 xmlFree(copy);
544 xmlFree(copy2);
545 return(-1);
546 }
547 memcpy(copy3, key3, lengths[2] + 1);
548 } else {
549 copy3 = NULL;
550 }
551 }
552
553 /*
554 * Shift the remainder of the probe sequence to the right
555 */
556 if (entry->hashValue != 0) {
557 const xmlHashEntry *end = &hash->table[hash->size];
558 const xmlHashEntry *cur = entry;
559
560 do {
561 cur++;
562 if (cur >= end)
563 cur = hash->table;
564 } while (cur->hashValue != 0);
565
566 if (cur < entry) {
567 /*
568 * If we traversed the end of the buffer, handle the part
569 * at the start of the buffer.
570 */
571 memmove(&hash->table[1], hash->table,
572 (char *) cur - (char *) hash->table);
573 cur = end - 1;
574 hash->table[0] = *cur;
575 }
576
577 memmove(&entry[1], entry, (char *) cur - (char *) entry);
578 }
579
580 /*
581 * Populate entry
582 */
583 entry->key = copy;
584 entry->key2 = copy2;
585 entry->key3 = copy3;
586 entry->payload = payload;
587 /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
588 entry->hashValue = hashValue | MAX_HASH_SIZE;
589
590 hash->nbElems++;
591
592 return(0);
593}
594
595/**
596 * xmlHashDefaultDeallocator:
597 * @entry: hash table entry
598 * @key: the entry's string key
599 *
600 * Free a hash table entry with xmlFree.
601 */
602void
603xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
604 xmlFree(entry);
605}
606
607/**
608 * xmlHashAddEntry:
609 * @hash: hash table
610 * @key: string key
611 * @payload: pointer to the payload
612 *
613 * Add a hash table entry. If an entry with this key already exists,
614 * payload will not be updated and -1 is returned. This return value
615 * can't be distinguished from out-of-memory errors, so this function
616 * should be used with care.
617 *
618 * Returns 0 on success and -1 in case of error.
619 */
620int
621xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
622 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
623}
624
625/**
626 * xmlHashAddEntry2:
627 * @hash: hash table
628 * @key: first string key
629 * @key2: second string key
630 * @payload: pointer to the payload
631 *
632 * Add a hash table entry with two strings as key.
633 *
634 * See xmlHashAddEntry.
635 *
636 * Returns 0 on success and -1 in case of error.
637 */
638int
639xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
640 const xmlChar *key2, void *payload) {
641 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
642}
643
644/**
645 * xmlHashAddEntry3:
646 * @hash: hash table
647 * @key: first string key
648 * @key2: second string key
649 * @key3: third string key
650 * @payload: pointer to the payload
651 *
652 * Add a hash table entry with three strings as key.
653 *
654 * See xmlHashAddEntry.
655 *
656 * Returns 0 on success and -1 in case of error.
657 */
658int
659xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
660 const xmlChar *key2, const xmlChar *key3,
661 void *payload) {
662 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
663}
664
665/**
666 * xmlHashUpdateEntry:
667 * @hash: hash table
668 * @key: string key
669 * @payload: pointer to the payload
670 * @dealloc: deallocator function for replaced item or NULL
671 *
672 * Add a hash table entry. If an entry with this key already exists,
673 * the old payload will be freed and updated with the new value.
674 *
675 * Returns 0 in case of success, -1 if a memory allocation failed.
676 */
677int
678xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
679 void *payload, xmlHashDeallocator dealloc) {
680 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
681 dealloc, 1));
682}
683
684/**
685 * xmlHashUpdateEntry2:
686 * @hash: hash table
687 * @key: first string key
688 * @key2: second string key
689 * @payload: pointer to the payload
690 * @dealloc: deallocator function for replaced item or NULL
691 *
692 * Add a hash table entry with two strings as key.
693 *
694 * See xmlHashUpdateEntry.
695 *
696 * Returns 0 on success and -1 in case of error.
697 */
698int
699xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
700 const xmlChar *key2, void *payload,
701 xmlHashDeallocator dealloc) {
702 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload,
703 dealloc, 1));
704}
705
706/**
707 * xmlHashUpdateEntry3:
708 * @hash: hash table
709 * @key: first string key
710 * @key2: second string key
711 * @key3: third string key
712 * @payload: pointer to the payload
713 * @dealloc: deallocator function for replaced item or NULL
714 *
715 * Add a hash table entry with three strings as key.
716 *
717 * See xmlHashUpdateEntry.
718 *
719 * Returns 0 on success and -1 in case of error.
720 */
721int
722xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
723 const xmlChar *key2, const xmlChar *key3,
724 void *payload, xmlHashDeallocator dealloc) {
725 return(xmlHashUpdateInternal(hash, key, key2, key3, payload,
726 dealloc, 1));
727}
728
729/**
730 * xmlHashLookup:
731 * @hash: hash table
732 * @key: string key
733 *
734 * Find the entry specified by @key.
735 *
736 * Returns a pointer to the payload or NULL if no entry was found.
737 */
738void *
739xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
740 return(xmlHashLookup3(hash, key, NULL, NULL));
741}
742
743/**
744 * xmlHashLookup2:
745 * @hash: hash table
746 * @key: first string key
747 * @key2: second string key
748 *
749 * Find the payload specified by the (@key, @key2) tuple.
750 *
751 * Returns a pointer to the payload or NULL if no entry was found.
752 */
753void *
754xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
755 const xmlChar *key2) {
756 return(xmlHashLookup3(hash, key, key2, NULL));
757}
758
759/**
760 * xmlHashQLookup:
761 * @hash: hash table
762 * @prefix: prefix of the string key
763 * @name: local name of the string key
764 *
765 * Find the payload specified by the QName @prefix:@name or @name.
766 *
767 * Returns a pointer to the payload or NULL if no entry was found.
768 */
769void *
770xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
771 const xmlChar *name) {
772 return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
773}
774
775/**
776 * xmlHashQLookup2:
777 * @hash: hash table
778 * @prefix: first prefix
779 * @name: first local name
780 * @prefix2: second prefix
781 * @name2: second local name
782 *
783 * Find the payload specified by the QNames tuple.
784 *
785 * Returns a pointer to the payload or NULL if no entry was found.
786 */
787void *
788xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
789 const xmlChar *name, const xmlChar *prefix2,
790 const xmlChar *name2) {
791 return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
792}
793
794/**
795 * xmlHashLookup3:
796 * @hash: hash table
797 * @key: first string key
798 * @key2: second string key
799 * @key3: third string key
800 *
801 * Find the payload specified by the (@key, @key2, @key3) tuple.
802 *
803 * Returns a pointer to the payload or NULL if no entry was found.
804 */
805void *
806xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
807 const xmlChar *key2, const xmlChar *key3) {
808 const xmlHashEntry *entry;
809 unsigned hashValue;
810 int found;
811
812 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
813 return(NULL);
814 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
815 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
816 if (found)
817 return(entry->payload);
818 return(NULL);
819}
820
821/**
822 * xmlHashQLookup3:
823 * @hash: hash table
824 * @prefix: first prefix
825 * @name: first local name
826 * @prefix2: second prefix
827 * @name2: second local name
828 * @prefix3: third prefix
829 * @name3: third local name
830 *
831 * Find the payload specified by the QNames tuple.
832 *
833 * Returns a pointer to the payload or NULL if no entry was found.
834 */
835ATTRIBUTE_NO_SANITIZE_INTEGER
836void *
837xmlHashQLookup3(xmlHashTablePtr hash,
838 const xmlChar *prefix, const xmlChar *name,
839 const xmlChar *prefix2, const xmlChar *name2,
840 const xmlChar *prefix3, const xmlChar *name3) {
841 const xmlHashEntry *entry;
842 unsigned hashValue, mask, pos, displ;
843
844 if ((hash == NULL) || (hash->size == 0) || (name == NULL))
845 return(NULL);
846
847 hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
848 name2, prefix3, name3);
849 mask = hash->size - 1;
850 pos = hashValue & mask;
851 entry = &hash->table[pos];
852
853 if (entry->hashValue != 0) {
854 displ = 0;
855 hashValue |= MAX_HASH_SIZE;
856
857 do {
858 if ((hashValue == entry->hashValue) &&
859 (xmlStrQEqual(prefix, name, entry->key)) &&
860 (xmlStrQEqual(prefix2, name2, entry->key2)) &&
861 (xmlStrQEqual(prefix3, name3, entry->key3)))
862 return(entry->payload);
863
864 displ++;
865 pos++;
866 entry++;
867 if ((pos & mask) == 0)
868 entry = hash->table;
869 } while ((entry->hashValue != 0) &&
870 (((pos - entry->hashValue) & mask) >= displ));
871 }
872
873 return(NULL);
874}
875
876typedef struct {
877 xmlHashScanner scan;
878 void *data;
879} stubData;
880
881static void
882stubHashScannerFull(void *payload, void *data, const xmlChar *key,
883 const xmlChar *key2 ATTRIBUTE_UNUSED,
884 const xmlChar *key3 ATTRIBUTE_UNUSED) {
885 stubData *sdata = (stubData *) data;
886 sdata->scan(payload, sdata->data, key);
887}
888
889/**
890 * xmlHashScan:
891 * @hash: hash table
892 * @scan: scanner function for items in the hash
893 * @data: extra data passed to @scan
894 *
895 * Scan the hash @table and apply @scan to each value.
896 */
897void
898xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
899 stubData sdata;
900 sdata.data = data;
901 sdata.scan = scan;
902 xmlHashScanFull(hash, stubHashScannerFull, &sdata);
903}
904
905/**
906 * xmlHashScanFull:
907 * @hash: hash table
908 * @scan: scanner function for items in the hash
909 * @data: extra data passed to @scan
910 *
911 * Scan the hash @table and apply @scan to each value.
912 */
913void
914xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
915 const xmlHashEntry *entry, *end;
916 xmlHashEntry old;
917 unsigned i;
918
919 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
920 return;
921
922 /*
923 * We must handle the case that a scanned entry is removed when executing
924 * the callback (xmlCleanSpecialAttr and possibly other places).
925 *
926 * Find the start of a probe sequence to avoid scanning entries twice if
927 * a deletion happens.
928 */
929 entry = hash->table;
930 end = &hash->table[hash->size];
931 while (entry->hashValue != 0) {
932 if (++entry >= end)
933 entry = hash->table;
934 }
935
936 for (i = 0; i < hash->size; i++) {
937 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
938 /*
939 * Make sure to rescan after a possible deletion.
940 */
941 do {
942 old = *entry;
943 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
944 } while ((entry->hashValue != 0) &&
945 (entry->payload != NULL) &&
946 ((entry->key != old.key) ||
947 (entry->key2 != old.key2) ||
948 (entry->key3 != old.key3)));
949 }
950 if (++entry >= end)
951 entry = hash->table;
952 }
953}
954
955/**
956 * xmlHashScan3:
957 * @hash: hash table
958 * @key: first string key or NULL
959 * @key2: second string key or NULL
960 * @key3: third string key or NULL
961 * @scan: scanner function for items in the hash
962 * @data: extra data passed to @scan
963 *
964 * Scan the hash @table and apply @scan to each value matching
965 * (@key, @key2, @key3) tuple. If one of the keys is null,
966 * the comparison is considered to match.
967 */
968void
969xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
970 const xmlChar *key2, const xmlChar *key3,
971 xmlHashScanner scan, void *data) {
972 stubData sdata;
973 sdata.data = data;
974 sdata.scan = scan;
975 xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
976}
977
978/**
979 * xmlHashScanFull3:
980 * @hash: hash table
981 * @key: first string key or NULL
982 * @key2: second string key or NULL
983 * @key3: third string key or NULL
984 * @scan: scanner function for items in the hash
985 * @data: extra data passed to @scan
986 *
987 * Scan the hash @table and apply @scan to each value matching
988 * (@key, @key2, @key3) tuple. If one of the keys is null,
989 * the comparison is considered to match.
990 */
991void
992xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
993 const xmlChar *key2, const xmlChar *key3,
994 xmlHashScannerFull scan, void *data) {
995 const xmlHashEntry *entry, *end;
996 xmlHashEntry old;
997 unsigned i;
998
999 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1000 return;
1001
1002 /*
1003 * We must handle the case that a scanned entry is removed when executing
1004 * the callback (xmlCleanSpecialAttr and possibly other places).
1005 *
1006 * Find the start of a probe sequence to avoid scanning entries twice if
1007 * a deletion happens.
1008 */
1009 entry = hash->table;
1010 end = &hash->table[hash->size];
1011 while (entry->hashValue != 0) {
1012 if (++entry >= end)
1013 entry = hash->table;
1014 }
1015
1016 for (i = 0; i < hash->size; i++) {
1017 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1018 /*
1019 * Make sure to rescan after a possible deletion.
1020 */
1021 do {
1022 if (((key != NULL) && (strcmp((const char *) key,
1023 (const char *) entry->key) != 0)) ||
1024 ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
1025 ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
1026 break;
1027 old = *entry;
1028 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1029 } while ((entry->hashValue != 0) &&
1030 (entry->payload != NULL) &&
1031 ((entry->key != old.key) ||
1032 (entry->key2 != old.key2) ||
1033 (entry->key3 != old.key3)));
1034 }
1035 if (++entry >= end)
1036 entry = hash->table;
1037 }
1038}
1039
1040/**
1041 * xmlHashCopy:
1042 * @hash: hash table
1043 * @copy: copier function for items in the hash
1044 *
1045 * Copy the hash @table using @copy to copy payloads.
1046 *
1047 * Returns the new table or NULL if a memory allocation failed.
1048 */
1049xmlHashTablePtr
1050xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
1051 const xmlHashEntry *entry, *end;
1052 xmlHashTablePtr ret;
1053
1054 if ((hash == NULL) || (copy == NULL))
1055 return(NULL);
1056
1057 ret = xmlHashCreate(hash->size);
1058 if (ret == NULL)
1059 return(NULL);
1060
1061 if (hash->size == 0)
1062 return(ret);
1063
1064 end = &hash->table[hash->size];
1065
1066 for (entry = hash->table; entry < end; entry++) {
1067 if (entry->hashValue != 0)
1068 xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3,
1069 copy(entry->payload, entry->key));
1070 }
1071
1072 return(ret);
1073}
1074
1075/**
1076 * xmlHashSize:
1077 * @hash: hash table
1078 *
1079 * Query the number of elements in the hash table.
1080 *
1081 * Returns the number of elements in the hash table or
1082 * -1 in case of error.
1083 */
1084int
1085xmlHashSize(xmlHashTablePtr hash) {
1086 if (hash == NULL)
1087 return(-1);
1088 return(hash->nbElems);
1089}
1090
1091/**
1092 * xmlHashRemoveEntry:
1093 * @hash: hash table
1094 * @key: string key
1095 * @dealloc: deallocator function for removed item or NULL
1096 *
1097 * Find the entry specified by the @key and remove it from the hash table.
1098 * Payload will be freed with @dealloc.
1099 *
1100 * Returns 0 on success and -1 if no entry was found.
1101 */
1102int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1103 xmlHashDeallocator dealloc) {
1104 return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1105}
1106
1107/**
1108 * xmlHashRemoveEntry2:
1109 * @hash: hash table
1110 * @key: first string key
1111 * @key2: second string key
1112 * @dealloc: deallocator function for removed item or NULL
1113 *
1114 * Remove an entry with two strings as key.
1115 *
1116 * See xmlHashRemoveEntry.
1117 *
1118 * Returns 0 on success and -1 in case of error.
1119 */
1120int
1121xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1122 const xmlChar *key2, xmlHashDeallocator dealloc) {
1123 return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1124}
1125
1126/**
1127 * xmlHashRemoveEntry3:
1128 * @hash: hash table
1129 * @key: first string key
1130 * @key2: second string key
1131 * @key3: third string key
1132 * @dealloc: deallocator function for removed item or NULL
1133 *
1134 * Remove an entry with three strings as key.
1135 *
1136 * See xmlHashRemoveEntry.
1137 *
1138 * Returns 0 on success and -1 in case of error.
1139 */
1140ATTRIBUTE_NO_SANITIZE_INTEGER
1141int
1142xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1143 const xmlChar *key2, const xmlChar *key3,
1144 xmlHashDeallocator dealloc) {
1145 xmlHashEntry *entry, *cur, *next;
1146 unsigned hashValue, mask, pos, nextpos;
1147 int found;
1148
1149 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1150 return(-1);
1151
1152 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1153 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1154 if (!found)
1155 return(-1);
1156
1157 if ((dealloc != NULL) && (entry->payload != NULL))
1158 dealloc(entry->payload, entry->key);
1159 if (hash->dict == NULL) {
1160 if (entry->key)
1161 xmlFree(entry->key);
1162 if (entry->key2)
1163 xmlFree(entry->key2);
1164 if (entry->key3)
1165 xmlFree(entry->key3);
1166 }
1167
1168 /*
1169 * Find end of probe sequence. Entries at their initial probe
1170 * position start a new sequence.
1171 */
1172 mask = hash->size - 1;
1173 pos = entry - hash->table;
1174 cur = entry;
1175
1176 while (1) {
1177 nextpos = pos + 1;
1178 next = cur + 1;
1179 if ((nextpos & mask) == 0)
1180 next = hash->table;
1181
1182 if ((next->hashValue == 0) ||
1183 (((next->hashValue - nextpos) & mask) == 0))
1184 break;
1185
1186 cur = next;
1187 pos = nextpos;
1188 }
1189
1190 /*
1191 * Backward shift
1192 */
1193 next = entry + 1;
1194
1195 if (cur < entry) {
1196 xmlHashEntry *end = &hash->table[hash->size];
1197
1198 memmove(entry, next, (char *) end - (char *) next);
1199 entry = hash->table;
1200 end[-1] = *entry;
1201 next = entry + 1;
1202 }
1203
1204 memmove(entry, next, (char *) cur - (char *) entry);
1205
1206 /*
1207 * Update entry
1208 */
1209 cur->hashValue = 0;
1210
1211 hash->nbElems--;
1212
1213 return(0);
1214}
1215
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use