xref: /aosp_15_r20/external/libxml2/hash.c (revision 7c5688314b92172186c154356a6374bf7684c3ca)
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] = {0, 0, 0};
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  */
595 void
xmlHashDefaultDeallocator(void * entry,const xmlChar * key ATTRIBUTE_UNUSED)596 xmlHashDefaultDeallocator(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  */
615 int
xmlHashAdd(xmlHashTablePtr hash,const xmlChar * key,void * payload)616 xmlHashAdd(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  */
635 int
xmlHashAdd2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload)636 xmlHashAdd2(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  */
657 int
xmlHashAdd3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload)658 xmlHashAdd3(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  */
680 int
xmlHashAddEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload)681 xmlHashAddEntry(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  */
705 int
xmlHashAddEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload)706 xmlHashAddEntry2(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  */
732 int
xmlHashAddEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload)733 xmlHashAddEntry3(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  */
758 int
xmlHashUpdateEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload,xmlHashDeallocator dealloc)759 xmlHashUpdateEntry(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  */
784 int
xmlHashUpdateEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload,xmlHashDeallocator dealloc)785 xmlHashUpdateEntry2(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  */
812 int
xmlHashUpdateEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload,xmlHashDeallocator dealloc)813 xmlHashUpdateEntry3(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  */
834 void *
xmlHashLookup(xmlHashTablePtr hash,const xmlChar * key)835 xmlHashLookup(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  */
849 void *
xmlHashLookup2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2)850 xmlHashLookup2(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  */
865 void *
xmlHashQLookup(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name)866 xmlHashQLookup(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  */
883 void *
xmlHashQLookup2(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2)884 xmlHashQLookup2(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  */
901 void *
xmlHashLookup3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3)902 xmlHashLookup3(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  */
931 ATTRIBUTE_NO_SANITIZE_INTEGER
932 void *
xmlHashQLookup3(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2,const xmlChar * prefix3,const xmlChar * name3)933 xmlHashQLookup3(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 
972 typedef struct {
973     xmlHashScanner scan;
974     void *data;
975 } stubData;
976 
977 static void
stubHashScannerFull(void * payload,void * data,const xmlChar * key,const xmlChar * key2 ATTRIBUTE_UNUSED,const xmlChar * key3 ATTRIBUTE_UNUSED)978 stubHashScannerFull(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  */
993 void
xmlHashScan(xmlHashTablePtr hash,xmlHashScanner scan,void * data)994 xmlHashScan(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  */
1009 void
xmlHashScanFull(xmlHashTablePtr hash,xmlHashScannerFull scan,void * data)1010 xmlHashScanFull(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  */
1064 void
xmlHashScan3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScanner scan,void * data)1065 xmlHashScan3(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  */
1087 void
xmlHashScanFull3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScannerFull scan,void * data)1088 xmlHashScanFull3(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  */
1148 xmlHashTablePtr
xmlHashCopySafe(xmlHashTablePtr hash,xmlHashCopier copyFunc,xmlHashDeallocator deallocFunc)1149 xmlHashCopySafe(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 
1184 error:
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  */
1200 xmlHashTablePtr
xmlHashCopy(xmlHashTablePtr hash,xmlHashCopier copy)1201 xmlHashCopy(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  */
1214 int
xmlHashSize(xmlHashTablePtr hash)1215 xmlHashSize(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  */
xmlHashRemoveEntry(xmlHashTablePtr hash,const xmlChar * key,xmlHashDeallocator dealloc)1232 int 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  */
1250 int
xmlHashRemoveEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,xmlHashDeallocator dealloc)1251 xmlHashRemoveEntry2(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  */
1270 ATTRIBUTE_NO_SANITIZE_INTEGER
1271 int
xmlHashRemoveEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashDeallocator dealloc)1272 xmlHashRemoveEntry3(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 
1346