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 */
36 typedef 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 */
48 struct _xmlHashTable {
49 xmlHashEntry *table;
50 unsigned size; /* power of two */
51 unsigned nbElems;
52 xmlDictPtr dict;
53 unsigned randomSeed;
54 };
55
56 static int
57 xmlHashGrow(xmlHashTablePtr hash, unsigned size);
58
59 ATTRIBUTE_NO_SANITIZE_INTEGER
60 static unsigned
xmlHashValue(unsigned seed,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,size_t * lengths)61 xmlHashValue(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
99 ATTRIBUTE_NO_SANITIZE_INTEGER
100 static unsigned
xmlHashQNameValue(unsigned seed,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2,const xmlChar * prefix3,const xmlChar * name3)101 xmlHashQNameValue(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 */
159 xmlHashTablePtr
xmlHashCreate(int size)160 xmlHashCreate(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 */
208 xmlHashTablePtr
xmlHashCreateDict(int size,xmlDictPtr dict)209 xmlHashCreateDict(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 */
228 void
xmlHashFree(xmlHashTablePtr hash,xmlHashDeallocator dealloc)229 xmlHashFree(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 */
268 static int
xmlFastStrEqual(const xmlChar * s1,const xmlChar * s2)269 xmlFastStrEqual(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 */
290 ATTRIBUTE_NO_SANITIZE_INTEGER
291 static xmlHashEntry *
xmlHashFindEntry(const xmlHashTable * hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,unsigned hashValue,int * pfound)292 xmlHashFindEntry(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 */
353 static int
xmlHashGrow(xmlHashTablePtr hash,unsigned size)354 xmlHashGrow(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
404 done:
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 */
423 ATTRIBUTE_NO_SANITIZE_INTEGER
424 static int
xmlHashUpdateInternal(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload,xmlHashDeallocator dealloc,int update)425 xmlHashUpdateInternal(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 */
602 void
xmlHashDefaultDeallocator(void * entry,const xmlChar * key ATTRIBUTE_UNUSED)603 xmlHashDefaultDeallocator(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 */
620 int
xmlHashAddEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload)621 xmlHashAddEntry(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 int
xmlHashAddEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload)637 xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
638 const xmlChar *key2, void *payload) {
639 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
640 }
641
642 /**
643 * xmlHashAddEntry3:
644 * @hash: hash table
645 * @key: first string key
646 * @key2: second string key
647 * @key3: third string key
648 * @payload: pointer to the payload
649 *
650 * Add a hash table entry with three strings as key.
651 *
652 * See xmlHashAddEntry.
653 */
654 int
xmlHashAddEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload)655 xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
656 const xmlChar *key2, const xmlChar *key3,
657 void *payload) {
658 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
659 }
660
661 /**
662 * xmlHashUpdateEntry:
663 * @hash: hash table
664 * @key: string key
665 * @payload: pointer to the payload
666 * @dealloc: deallocator function for replaced item or NULL
667 *
668 * Add a hash table entry. If an entry with this key already exists,
669 * the old payload will be freed and updated with the new value.
670 *
671 * Returns 0 in case of success, -1 if a memory allocation failed.
672 */
673 int
xmlHashUpdateEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload,xmlHashDeallocator dealloc)674 xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
675 void *payload, xmlHashDeallocator dealloc) {
676 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
677 dealloc, 1));
678 }
679
680 /**
681 * xmlHashUpdateEntry2:
682 * @hash: hash table
683 * @key: first string key
684 * @key2: second string key
685 * @payload: pointer to the payload
686 * @dealloc: deallocator function for replaced item or NULL
687 *
688 * Add a hash table entry with two strings as key.
689 *
690 * See xmlHashUpdateEntry.
691 */
692 int
xmlHashUpdateEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload,xmlHashDeallocator dealloc)693 xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
694 const xmlChar *key2, void *payload,
695 xmlHashDeallocator dealloc) {
696 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload,
697 dealloc, 1));
698 }
699
700 /**
701 * xmlHashUpdateEntry3:
702 * @hash: hash table
703 * @key: first string key
704 * @key2: second string key
705 * @key3: third string key
706 * @payload: pointer to the payload
707 * @dealloc: deallocator function for replaced item or NULL
708 *
709 * Add a hash table entry with three strings as key.
710 *
711 * See xmlHashUpdateEntry.
712 */
713 int
xmlHashUpdateEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload,xmlHashDeallocator dealloc)714 xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
715 const xmlChar *key2, const xmlChar *key3,
716 void *payload, xmlHashDeallocator dealloc) {
717 return(xmlHashUpdateInternal(hash, key, key2, key3, payload,
718 dealloc, 1));
719 }
720
721 /**
722 * xmlHashLookup:
723 * @hash: hash table
724 * @key: string key
725 *
726 * Find the entry specified by @key.
727 *
728 * Returns a pointer to the payload or NULL if no entry was found.
729 */
730 void *
xmlHashLookup(xmlHashTablePtr hash,const xmlChar * key)731 xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
732 return(xmlHashLookup3(hash, key, NULL, NULL));
733 }
734
735 /**
736 * xmlHashLookup2:
737 * @hash: hash table
738 * @key: first string key
739 * @key2: second string key
740 *
741 * Find the payload specified by the (@key, @key2) tuple.
742 *
743 * Returns a pointer to the payload or NULL if no entry was found.
744 */
745 void *
xmlHashLookup2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2)746 xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
747 const xmlChar *key2) {
748 return(xmlHashLookup3(hash, key, key2, NULL));
749 }
750
751 /**
752 * xmlHashQLookup:
753 * @hash: hash table
754 * @prefix: prefix of the string key
755 * @name: local name of the string key
756 *
757 * Find the payload specified by the QName @prefix:@name or @name.
758 *
759 * Returns a pointer to the payload or NULL if no entry was found.
760 */
761 void *
xmlHashQLookup(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name)762 xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
763 const xmlChar *name) {
764 return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
765 }
766
767 /**
768 * xmlHashQLookup2:
769 * @hash: hash table
770 * @prefix: first prefix
771 * @name: first local name
772 * @prefix2: second prefix
773 * @name2: second local name
774 *
775 * Find the payload specified by the QNames tuple.
776 *
777 * Returns a pointer to the payload or NULL if no entry was found.
778 */
779 void *
xmlHashQLookup2(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2)780 xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
781 const xmlChar *name, const xmlChar *prefix2,
782 const xmlChar *name2) {
783 return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
784 }
785
786 /**
787 * xmlHashLookup3:
788 * @hash: hash table
789 * @key: first string key
790 * @key2: second string key
791 * @key3: third string key
792 *
793 * Find the payload specified by the (@key, @key2, @key3) tuple.
794 *
795 * Returns a pointer to the payload or NULL if no entry was found.
796 */
797 void *
xmlHashLookup3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3)798 xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
799 const xmlChar *key2, const xmlChar *key3) {
800 const xmlHashEntry *entry;
801 unsigned hashValue;
802 int found;
803
804 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
805 return(NULL);
806 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
807 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
808 if (found)
809 return(entry->payload);
810 return(NULL);
811 }
812
813 /**
814 * xmlHashQLookup3:
815 * @hash: hash table
816 * @prefix: first prefix
817 * @name: first local name
818 * @prefix2: second prefix
819 * @name2: second local name
820 * @prefix3: third prefix
821 * @name3: third local name
822 *
823 * Find the payload specified by the QNames tuple.
824 *
825 * Returns a pointer to the payload or NULL if no entry was found.
826 */
827 ATTRIBUTE_NO_SANITIZE_INTEGER
828 void *
xmlHashQLookup3(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2,const xmlChar * prefix3,const xmlChar * name3)829 xmlHashQLookup3(xmlHashTablePtr hash,
830 const xmlChar *prefix, const xmlChar *name,
831 const xmlChar *prefix2, const xmlChar *name2,
832 const xmlChar *prefix3, const xmlChar *name3) {
833 const xmlHashEntry *entry;
834 unsigned hashValue, mask, pos, displ;
835
836 if ((hash == NULL) || (hash->size == 0) || (name == NULL))
837 return(NULL);
838
839 hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
840 name2, prefix3, name3);
841 mask = hash->size - 1;
842 pos = hashValue & mask;
843 entry = &hash->table[pos];
844
845 if (entry->hashValue != 0) {
846 displ = 0;
847 hashValue |= MAX_HASH_SIZE;
848
849 do {
850 if ((hashValue == entry->hashValue) &&
851 (xmlStrQEqual(prefix, name, entry->key)) &&
852 (xmlStrQEqual(prefix2, name2, entry->key2)) &&
853 (xmlStrQEqual(prefix3, name3, entry->key3)))
854 return(entry->payload);
855
856 displ++;
857 pos++;
858 entry++;
859 if ((pos & mask) == 0)
860 entry = hash->table;
861 } while ((entry->hashValue != 0) &&
862 (((pos - entry->hashValue) & mask) >= displ));
863 }
864
865 return(NULL);
866 }
867
868 typedef struct {
869 xmlHashScanner scan;
870 void *data;
871 } stubData;
872
873 static void
stubHashScannerFull(void * payload,void * data,const xmlChar * key,const xmlChar * key2 ATTRIBUTE_UNUSED,const xmlChar * key3 ATTRIBUTE_UNUSED)874 stubHashScannerFull(void *payload, void *data, const xmlChar *key,
875 const xmlChar *key2 ATTRIBUTE_UNUSED,
876 const xmlChar *key3 ATTRIBUTE_UNUSED) {
877 stubData *sdata = (stubData *) data;
878 sdata->scan(payload, sdata->data, key);
879 }
880
881 /**
882 * xmlHashScan:
883 * @hash: hash table
884 * @scan: scanner function for items in the hash
885 * @data: extra data passed to @scan
886 *
887 * Scan the hash @table and apply @scan to each value.
888 */
889 void
xmlHashScan(xmlHashTablePtr hash,xmlHashScanner scan,void * data)890 xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
891 stubData sdata;
892 sdata.data = data;
893 sdata.scan = scan;
894 xmlHashScanFull(hash, stubHashScannerFull, &sdata);
895 }
896
897 /**
898 * xmlHashScanFull:
899 * @hash: hash table
900 * @scan: scanner function for items in the hash
901 * @data: extra data passed to @scan
902 *
903 * Scan the hash @table and apply @scan to each value.
904 */
905 void
xmlHashScanFull(xmlHashTablePtr hash,xmlHashScannerFull scan,void * data)906 xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
907 const xmlHashEntry *entry, *end;
908
909 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
910 return;
911
912 end = &hash->table[hash->size];
913
914 for (entry = hash->table; entry < end; entry++) {
915 if ((entry->hashValue != 0) && (entry->payload != NULL))
916 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
917 }
918 }
919
920 /**
921 * xmlHashScan3:
922 * @hash: hash table
923 * @key: first string key or NULL
924 * @key2: second string key or NULL
925 * @key3: third string key or NULL
926 * @scan: scanner function for items in the hash
927 * @data: extra data passed to @scan
928 *
929 * Scan the hash @table and apply @scan to each value matching
930 * (@key, @key2, @key3) tuple. If one of the keys is null,
931 * the comparison is considered to match.
932 */
933 void
xmlHashScan3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScanner scan,void * data)934 xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
935 const xmlChar *key2, const xmlChar *key3,
936 xmlHashScanner scan, void *data) {
937 stubData sdata;
938 sdata.data = data;
939 sdata.scan = scan;
940 xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
941 }
942
943 /**
944 * xmlHashScanFull3:
945 * @hash: hash table
946 * @key: first string key or NULL
947 * @key2: second string key or NULL
948 * @key3: third string key or NULL
949 * @scan: scanner function for items in the hash
950 * @data: extra data passed to @scan
951 *
952 * Scan the hash @table and apply @scan to each value matching
953 * (@key, @key2, @key3) tuple. If one of the keys is null,
954 * the comparison is considered to match.
955 */
956 void
xmlHashScanFull3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScannerFull scan,void * data)957 xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
958 const xmlChar *key2, const xmlChar *key3,
959 xmlHashScannerFull scan, void *data) {
960 const xmlHashEntry *entry, *end;
961
962 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
963 return;
964
965 end = &hash->table[hash->size];
966
967 for (entry = hash->table; entry < end; entry++) {
968 if (entry->hashValue == 0)
969 continue;
970 if (((key == NULL) ||
971 (strcmp((const char *) key, (const char *) entry->key) == 0)) &&
972 ((key2 == NULL) || (xmlFastStrEqual(key2, entry->key2))) &&
973 ((key3 == NULL) || (xmlFastStrEqual(key3, entry->key3))) &&
974 (entry->payload != NULL)) {
975 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
976 }
977 }
978 }
979
980 /**
981 * xmlHashCopy:
982 * @hash: hash table
983 * @copy: copier function for items in the hash
984 *
985 * Copy the hash @table using @copy to copy payloads.
986 *
987 * Returns the new table or NULL if a memory allocation failed.
988 */
989 xmlHashTablePtr
xmlHashCopy(xmlHashTablePtr hash,xmlHashCopier copy)990 xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
991 const xmlHashEntry *entry, *end;
992 xmlHashTablePtr ret;
993
994 if ((hash == NULL) || (copy == NULL))
995 return(NULL);
996
997 ret = xmlHashCreate(hash->size);
998 if (ret == NULL)
999 return(NULL);
1000
1001 if (hash->size == 0)
1002 return(ret);
1003
1004 end = &hash->table[hash->size];
1005
1006 for (entry = hash->table; entry < end; entry++) {
1007 if (entry->hashValue != 0)
1008 xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3,
1009 copy(entry->payload, entry->key));
1010 }
1011
1012 return(ret);
1013 }
1014
1015 /**
1016 * xmlHashSize:
1017 * @hash: hash table
1018 *
1019 * Query the number of elements in the hash table.
1020 *
1021 * Returns the number of elements in the hash table or
1022 * -1 in case of error.
1023 */
1024 int
xmlHashSize(xmlHashTablePtr hash)1025 xmlHashSize(xmlHashTablePtr hash) {
1026 if (hash == NULL)
1027 return(-1);
1028 return(hash->nbElems);
1029 }
1030
1031 /**
1032 * xmlHashRemoveEntry:
1033 * @hash: hash table
1034 * @key: string key
1035 * @dealloc: deallocator function for removed item or NULL
1036 *
1037 * Find the entry specified by the @key and remove it from the hash table.
1038 * Payload will be freed with @dealloc.
1039 *
1040 * Returns 0 on success and -1 if no entry was found.
1041 */
xmlHashRemoveEntry(xmlHashTablePtr hash,const xmlChar * key,xmlHashDeallocator dealloc)1042 int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1043 xmlHashDeallocator dealloc) {
1044 return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1045 }
1046
1047 /**
1048 * xmlHashRemoveEntry2:
1049 * @hash: hash table
1050 * @key: first string key
1051 * @key2: second string key
1052 * @dealloc: deallocator function for removed item or NULL
1053 *
1054 * Remove an entry with two strings as key.
1055 *
1056 * See xmlHashRemoveEntry.
1057 */
1058 int
xmlHashRemoveEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,xmlHashDeallocator dealloc)1059 xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1060 const xmlChar *key2, xmlHashDeallocator dealloc) {
1061 return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1062 }
1063
1064 /**
1065 * xmlHashRemoveEntry3:
1066 * @hash: hash table
1067 * @key: first string key
1068 * @key2: second string key
1069 * @key3: third string key
1070 * @dealloc: deallocator function for removed item or NULL
1071 *
1072 * Remove an entry with three strings as key.
1073 *
1074 * See xmlHashRemoveEntry.
1075 */
1076 ATTRIBUTE_NO_SANITIZE_INTEGER
1077 int
xmlHashRemoveEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashDeallocator dealloc)1078 xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1079 const xmlChar *key2, const xmlChar *key3,
1080 xmlHashDeallocator dealloc) {
1081 xmlHashEntry *entry, *cur, *next;
1082 unsigned hashValue, mask, pos, nextpos;
1083 int found;
1084
1085 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1086 return(-1);
1087
1088 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1089 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1090 if (!found)
1091 return(-1);
1092
1093 if ((dealloc != NULL) && (entry->payload != NULL))
1094 dealloc(entry->payload, entry->key);
1095 if (hash->dict == NULL) {
1096 if (entry->key)
1097 xmlFree(entry->key);
1098 if (entry->key2)
1099 xmlFree(entry->key2);
1100 if (entry->key3)
1101 xmlFree(entry->key3);
1102 }
1103
1104 /*
1105 * Find end of probe sequence. Entries at their initial probe
1106 * position start a new sequence.
1107 */
1108 mask = hash->size - 1;
1109 pos = entry - hash->table;
1110 cur = entry;
1111
1112 while (1) {
1113 nextpos = pos + 1;
1114 next = cur + 1;
1115 if ((nextpos & mask) == 0)
1116 next = hash->table;
1117
1118 if ((next->hashValue == 0) ||
1119 (((next->hashValue - nextpos) & mask) == 0))
1120 break;
1121
1122 cur = next;
1123 pos = nextpos;
1124 }
1125
1126 /*
1127 * Backward shift
1128 */
1129 next = entry + 1;
1130
1131 if (cur < entry) {
1132 xmlHashEntry *end = &hash->table[hash->size];
1133
1134 memmove(entry, next, (char *) end - (char *) next);
1135 entry = hash->table;
1136 end[-1] = *entry;
1137 next = entry + 1;
1138 }
1139
1140 memmove(entry, next, (char *) cur - (char *) entry);
1141
1142 /*
1143 * Update entry
1144 */
1145 cur->hashValue = 0;
1146
1147 hash->nbElems--;
1148
1149 return(0);
1150 }
1151
1152