• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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