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 }
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 * Returns 1 on success, 0 if an entry exists and -1 in case of error.
612 */
613 int
xmlHashAdd(xmlHashTablePtr hash,const xmlChar * key,void * payload)614 xmlHashAdd(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
615 return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
616 }
617
618 /**
619 * xmlHashAdd2:
620 * @hash: hash table
621 * @key: first string key
622 * @key2: second string key
623 * @payload: pointer to the payload
624 *
625 * Add a hash table entry with two strings as key.
626 *
627 * See xmlHashAdd.
628 */
629 int
xmlHashAdd2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload)630 xmlHashAdd2(xmlHashTablePtr hash, const xmlChar *key,
631 const xmlChar *key2, void *payload) {
632 return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
633 }
634
635 /**
636 * xmlHashAdd3:
637 * @hash: hash table
638 * @key: first string key
639 * @key2: second string key
640 * @key3: third string key
641 * @payload: pointer to the payload
642 *
643 * Add a hash table entry with three strings as key.
644 *
645 * See xmlHashAdd.
646 */
647 int
xmlHashAdd3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload)648 xmlHashAdd3(xmlHashTablePtr hash, const xmlChar *key,
649 const xmlChar *key2, const xmlChar *key3,
650 void *payload) {
651 return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
652 }
653
654 /**
655 * xmlHashAddEntry:
656 * @hash: hash table
657 * @key: string key
658 * @payload: pointer to the payload
659 *
660 * Add a hash table entry. If an entry with this key already exists,
661 * payload will not be updated and -1 is returned. This return value
662 * can't be distinguished from out-of-memory errors, so this function
663 * should be used with care.
664 *
665 * NOTE: This function doesn't allow to distinguish malloc failures from
666 * existing entries. Use xmlHashAdd instead.
667 *
668 * Returns 0 on success and -1 in case of error.
669 */
670 int
xmlHashAddEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload)671 xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
672 int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0);
673
674 if (res == 0)
675 res = -1;
676 else if (res == 1)
677 res = 0;
678
679 return(res);
680 }
681
682 /**
683 * xmlHashAddEntry2:
684 * @hash: hash table
685 * @key: first string key
686 * @key2: second string key
687 * @payload: pointer to the payload
688 *
689 * Add a hash table entry with two strings as key.
690 *
691 * See xmlHashAddEntry.
692 *
693 * Returns 0 on success and -1 in case of error.
694 */
695 int
xmlHashAddEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload)696 xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
697 const xmlChar *key2, void *payload) {
698 int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0);
699
700 if (res == 0)
701 res = -1;
702 else if (res == 1)
703 res = 0;
704
705 return(res);
706 }
707
708 /**
709 * xmlHashAddEntry3:
710 * @hash: hash table
711 * @key: first string key
712 * @key2: second string key
713 * @key3: third string key
714 * @payload: pointer to the payload
715 *
716 * Add a hash table entry with three strings as key.
717 *
718 * See xmlHashAddEntry.
719 *
720 * Returns 0 on success and -1 in case of error.
721 */
722 int
xmlHashAddEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload)723 xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
724 const xmlChar *key2, const xmlChar *key3,
725 void *payload) {
726 int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0);
727
728 if (res == 0)
729 res = -1;
730 else if (res == 1)
731 res = 0;
732
733 return(res);
734 }
735
736 /**
737 * xmlHashUpdateEntry:
738 * @hash: hash table
739 * @key: string key
740 * @payload: pointer to the payload
741 * @dealloc: deallocator function for replaced item or NULL
742 *
743 * Add a hash table entry. If an entry with this key already exists,
744 * the old payload will be freed and updated with the new value.
745 *
746 * Returns 0 in case of success, -1 if a memory allocation failed.
747 */
748 int
xmlHashUpdateEntry(xmlHashTablePtr hash,const xmlChar * key,void * payload,xmlHashDeallocator dealloc)749 xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
750 void *payload, xmlHashDeallocator dealloc) {
751 int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
752 dealloc, 1);
753
754 if (res == 1)
755 res = 0;
756
757 return(res);
758 }
759
760 /**
761 * xmlHashUpdateEntry2:
762 * @hash: hash table
763 * @key: first string key
764 * @key2: second string key
765 * @payload: pointer to the payload
766 * @dealloc: deallocator function for replaced item or NULL
767 *
768 * Add a hash table entry with two strings as key.
769 *
770 * See xmlHashUpdateEntry.
771 *
772 * Returns 0 on success and -1 in case of error.
773 */
774 int
xmlHashUpdateEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,void * payload,xmlHashDeallocator dealloc)775 xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
776 const xmlChar *key2, void *payload,
777 xmlHashDeallocator dealloc) {
778 int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload,
779 dealloc, 1);
780
781 if (res == 1)
782 res = 0;
783
784 return(res);
785 }
786
787 /**
788 * xmlHashUpdateEntry3:
789 * @hash: hash table
790 * @key: first string key
791 * @key2: second string key
792 * @key3: third string key
793 * @payload: pointer to the payload
794 * @dealloc: deallocator function for replaced item or NULL
795 *
796 * Add a hash table entry with three strings as key.
797 *
798 * See xmlHashUpdateEntry.
799 *
800 * Returns 0 on success and -1 in case of error.
801 */
802 int
xmlHashUpdateEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,void * payload,xmlHashDeallocator dealloc)803 xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
804 const xmlChar *key2, const xmlChar *key3,
805 void *payload, xmlHashDeallocator dealloc) {
806 int res = xmlHashUpdateInternal(hash, key, key2, key3, payload,
807 dealloc, 1);
808
809 if (res == 1)
810 res = 0;
811
812 return(res);
813 }
814
815 /**
816 * xmlHashLookup:
817 * @hash: hash table
818 * @key: string key
819 *
820 * Find the entry specified by @key.
821 *
822 * Returns a pointer to the payload or NULL if no entry was found.
823 */
824 void *
xmlHashLookup(xmlHashTablePtr hash,const xmlChar * key)825 xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
826 return(xmlHashLookup3(hash, key, NULL, NULL));
827 }
828
829 /**
830 * xmlHashLookup2:
831 * @hash: hash table
832 * @key: first string key
833 * @key2: second string key
834 *
835 * Find the payload specified by the (@key, @key2) tuple.
836 *
837 * Returns a pointer to the payload or NULL if no entry was found.
838 */
839 void *
xmlHashLookup2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2)840 xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
841 const xmlChar *key2) {
842 return(xmlHashLookup3(hash, key, key2, NULL));
843 }
844
845 /**
846 * xmlHashQLookup:
847 * @hash: hash table
848 * @prefix: prefix of the string key
849 * @name: local name of the string key
850 *
851 * Find the payload specified by the QName @prefix:@name or @name.
852 *
853 * Returns a pointer to the payload or NULL if no entry was found.
854 */
855 void *
xmlHashQLookup(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name)856 xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
857 const xmlChar *name) {
858 return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
859 }
860
861 /**
862 * xmlHashQLookup2:
863 * @hash: hash table
864 * @prefix: first prefix
865 * @name: first local name
866 * @prefix2: second prefix
867 * @name2: second local name
868 *
869 * Find the payload specified by the QNames tuple.
870 *
871 * Returns a pointer to the payload or NULL if no entry was found.
872 */
873 void *
xmlHashQLookup2(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2)874 xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
875 const xmlChar *name, const xmlChar *prefix2,
876 const xmlChar *name2) {
877 return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
878 }
879
880 /**
881 * xmlHashLookup3:
882 * @hash: hash table
883 * @key: first string key
884 * @key2: second string key
885 * @key3: third string key
886 *
887 * Find the payload specified by the (@key, @key2, @key3) tuple.
888 *
889 * Returns a pointer to the payload or NULL if no entry was found.
890 */
891 void *
xmlHashLookup3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3)892 xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
893 const xmlChar *key2, const xmlChar *key3) {
894 const xmlHashEntry *entry;
895 unsigned hashValue;
896 int found;
897
898 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
899 return(NULL);
900 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
901 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
902 if (found)
903 return(entry->payload);
904 return(NULL);
905 }
906
907 /**
908 * xmlHashQLookup3:
909 * @hash: hash table
910 * @prefix: first prefix
911 * @name: first local name
912 * @prefix2: second prefix
913 * @name2: second local name
914 * @prefix3: third prefix
915 * @name3: third local name
916 *
917 * Find the payload specified by the QNames tuple.
918 *
919 * Returns a pointer to the payload or NULL if no entry was found.
920 */
921 ATTRIBUTE_NO_SANITIZE_INTEGER
922 void *
xmlHashQLookup3(xmlHashTablePtr hash,const xmlChar * prefix,const xmlChar * name,const xmlChar * prefix2,const xmlChar * name2,const xmlChar * prefix3,const xmlChar * name3)923 xmlHashQLookup3(xmlHashTablePtr hash,
924 const xmlChar *prefix, const xmlChar *name,
925 const xmlChar *prefix2, const xmlChar *name2,
926 const xmlChar *prefix3, const xmlChar *name3) {
927 const xmlHashEntry *entry;
928 unsigned hashValue, mask, pos, displ;
929
930 if ((hash == NULL) || (hash->size == 0) || (name == NULL))
931 return(NULL);
932
933 hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
934 name2, prefix3, name3);
935 mask = hash->size - 1;
936 pos = hashValue & mask;
937 entry = &hash->table[pos];
938
939 if (entry->hashValue != 0) {
940 displ = 0;
941 hashValue |= MAX_HASH_SIZE;
942
943 do {
944 if ((hashValue == entry->hashValue) &&
945 (xmlStrQEqual(prefix, name, entry->key)) &&
946 (xmlStrQEqual(prefix2, name2, entry->key2)) &&
947 (xmlStrQEqual(prefix3, name3, entry->key3)))
948 return(entry->payload);
949
950 displ++;
951 pos++;
952 entry++;
953 if ((pos & mask) == 0)
954 entry = hash->table;
955 } while ((entry->hashValue != 0) &&
956 (((pos - entry->hashValue) & mask) >= displ));
957 }
958
959 return(NULL);
960 }
961
962 typedef struct {
963 xmlHashScanner scan;
964 void *data;
965 } stubData;
966
967 static void
stubHashScannerFull(void * payload,void * data,const xmlChar * key,const xmlChar * key2 ATTRIBUTE_UNUSED,const xmlChar * key3 ATTRIBUTE_UNUSED)968 stubHashScannerFull(void *payload, void *data, const xmlChar *key,
969 const xmlChar *key2 ATTRIBUTE_UNUSED,
970 const xmlChar *key3 ATTRIBUTE_UNUSED) {
971 stubData *sdata = (stubData *) data;
972 sdata->scan(payload, sdata->data, key);
973 }
974
975 /**
976 * xmlHashScan:
977 * @hash: hash table
978 * @scan: scanner function for items in the hash
979 * @data: extra data passed to @scan
980 *
981 * Scan the hash @table and apply @scan to each value.
982 */
983 void
xmlHashScan(xmlHashTablePtr hash,xmlHashScanner scan,void * data)984 xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
985 stubData sdata;
986 sdata.data = data;
987 sdata.scan = scan;
988 xmlHashScanFull(hash, stubHashScannerFull, &sdata);
989 }
990
991 /**
992 * xmlHashScanFull:
993 * @hash: hash table
994 * @scan: scanner function for items in the hash
995 * @data: extra data passed to @scan
996 *
997 * Scan the hash @table and apply @scan to each value.
998 */
999 void
xmlHashScanFull(xmlHashTablePtr hash,xmlHashScannerFull scan,void * data)1000 xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
1001 const xmlHashEntry *entry, *end;
1002 xmlHashEntry old;
1003 unsigned i;
1004
1005 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1006 return;
1007
1008 /*
1009 * We must handle the case that a scanned entry is removed when executing
1010 * the callback (xmlCleanSpecialAttr and possibly other places).
1011 *
1012 * Find the start of a probe sequence to avoid scanning entries twice if
1013 * a deletion happens.
1014 */
1015 entry = hash->table;
1016 end = &hash->table[hash->size];
1017 while (entry->hashValue != 0) {
1018 if (++entry >= end)
1019 entry = hash->table;
1020 }
1021
1022 for (i = 0; i < hash->size; i++) {
1023 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1024 /*
1025 * Make sure to rescan after a possible deletion.
1026 */
1027 do {
1028 old = *entry;
1029 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1030 } while ((entry->hashValue != 0) &&
1031 (entry->payload != NULL) &&
1032 ((entry->key != old.key) ||
1033 (entry->key2 != old.key2) ||
1034 (entry->key3 != old.key3)));
1035 }
1036 if (++entry >= end)
1037 entry = hash->table;
1038 }
1039 }
1040
1041 /**
1042 * xmlHashScan3:
1043 * @hash: hash table
1044 * @key: first string key or NULL
1045 * @key2: second string key or NULL
1046 * @key3: third string key or NULL
1047 * @scan: scanner function for items in the hash
1048 * @data: extra data passed to @scan
1049 *
1050 * Scan the hash @table and apply @scan to each value matching
1051 * (@key, @key2, @key3) tuple. If one of the keys is null,
1052 * the comparison is considered to match.
1053 */
1054 void
xmlHashScan3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScanner scan,void * data)1055 xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
1056 const xmlChar *key2, const xmlChar *key3,
1057 xmlHashScanner scan, void *data) {
1058 stubData sdata;
1059 sdata.data = data;
1060 sdata.scan = scan;
1061 xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
1062 }
1063
1064 /**
1065 * xmlHashScanFull3:
1066 * @hash: hash table
1067 * @key: first string key or NULL
1068 * @key2: second string key or NULL
1069 * @key3: third string key or NULL
1070 * @scan: scanner function for items in the hash
1071 * @data: extra data passed to @scan
1072 *
1073 * Scan the hash @table and apply @scan to each value matching
1074 * (@key, @key2, @key3) tuple. If one of the keys is null,
1075 * the comparison is considered to match.
1076 */
1077 void
xmlHashScanFull3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashScannerFull scan,void * data)1078 xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
1079 const xmlChar *key2, const xmlChar *key3,
1080 xmlHashScannerFull scan, void *data) {
1081 const xmlHashEntry *entry, *end;
1082 xmlHashEntry old;
1083 unsigned i;
1084
1085 if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
1086 return;
1087
1088 /*
1089 * We must handle the case that a scanned entry is removed when executing
1090 * the callback (xmlCleanSpecialAttr and possibly other places).
1091 *
1092 * Find the start of a probe sequence to avoid scanning entries twice if
1093 * a deletion happens.
1094 */
1095 entry = hash->table;
1096 end = &hash->table[hash->size];
1097 while (entry->hashValue != 0) {
1098 if (++entry >= end)
1099 entry = hash->table;
1100 }
1101
1102 for (i = 0; i < hash->size; i++) {
1103 if ((entry->hashValue != 0) && (entry->payload != NULL)) {
1104 /*
1105 * Make sure to rescan after a possible deletion.
1106 */
1107 do {
1108 if (((key != NULL) && (strcmp((const char *) key,
1109 (const char *) entry->key) != 0)) ||
1110 ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
1111 ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
1112 break;
1113 old = *entry;
1114 scan(entry->payload, data, entry->key, entry->key2, entry->key3);
1115 } while ((entry->hashValue != 0) &&
1116 (entry->payload != NULL) &&
1117 ((entry->key != old.key) ||
1118 (entry->key2 != old.key2) ||
1119 (entry->key3 != old.key3)));
1120 }
1121 if (++entry >= end)
1122 entry = hash->table;
1123 }
1124 }
1125
1126 /*
1127 * xmlHashCopySafe:
1128 * @hash: hash table
1129 * @copyFunc: copier function for items in the hash
1130 * @deallocFunc: deallocation function in case of errors
1131 *
1132 * Copy the hash table using @copyFunc to copy payloads.
1133 *
1134 * Returns the new table or NULL if a memory allocation failed.
1135 */
1136 xmlHashTablePtr
xmlHashCopySafe(xmlHashTablePtr hash,xmlHashCopier copyFunc,xmlHashDeallocator deallocFunc)1137 xmlHashCopySafe(xmlHashTablePtr hash, xmlHashCopier copyFunc,
1138 xmlHashDeallocator deallocFunc) {
1139 const xmlHashEntry *entry, *end;
1140 xmlHashTablePtr ret;
1141
1142 if ((hash == NULL) || (copyFunc == NULL))
1143 return(NULL);
1144
1145 ret = xmlHashCreate(hash->size);
1146 if (ret == NULL)
1147 return(NULL);
1148
1149 if (hash->size == 0)
1150 return(ret);
1151
1152 end = &hash->table[hash->size];
1153
1154 for (entry = hash->table; entry < end; entry++) {
1155 if (entry->hashValue != 0) {
1156 void *copy;
1157
1158 copy = copyFunc(entry->payload, entry->key);
1159 if (copy == NULL)
1160 goto error;
1161 if (xmlHashAdd3(ret, entry->key, entry->key2, entry->key3,
1162 copy) <= 0) {
1163 if (deallocFunc != NULL)
1164 deallocFunc(copy, entry->key);
1165 goto error;
1166 }
1167 }
1168 }
1169
1170 return(ret);
1171
1172 error:
1173 xmlHashFree(ret, deallocFunc);
1174 return(NULL);
1175 }
1176
1177 /*
1178 * xmlHashCopy:
1179 * @hash: hash table
1180 * @copy: copier function for items in the hash
1181 *
1182 * DEPRECATED: Leaks memory in error case.
1183 *
1184 * Copy the hash table using @copy to copy payloads.
1185 *
1186 * Returns the new table or NULL if a memory allocation failed.
1187 */
1188 xmlHashTablePtr
xmlHashCopy(xmlHashTablePtr hash,xmlHashCopier copy)1189 xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
1190 return(xmlHashCopySafe(hash, copy, NULL));
1191 }
1192
1193 /**
1194 * xmlHashSize:
1195 * @hash: hash table
1196 *
1197 * Query the number of elements in the hash table.
1198 *
1199 * Returns the number of elements in the hash table or
1200 * -1 in case of error.
1201 */
1202 int
xmlHashSize(xmlHashTablePtr hash)1203 xmlHashSize(xmlHashTablePtr hash) {
1204 if (hash == NULL)
1205 return(-1);
1206 return(hash->nbElems);
1207 }
1208
1209 /**
1210 * xmlHashRemoveEntry:
1211 * @hash: hash table
1212 * @key: string key
1213 * @dealloc: deallocator function for removed item or NULL
1214 *
1215 * Find the entry specified by the @key and remove it from the hash table.
1216 * Payload will be freed with @dealloc.
1217 *
1218 * Returns 0 on success and -1 if no entry was found.
1219 */
xmlHashRemoveEntry(xmlHashTablePtr hash,const xmlChar * key,xmlHashDeallocator dealloc)1220 int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
1221 xmlHashDeallocator dealloc) {
1222 return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
1223 }
1224
1225 /**
1226 * xmlHashRemoveEntry2:
1227 * @hash: hash table
1228 * @key: first string key
1229 * @key2: second string key
1230 * @dealloc: deallocator function for removed item or NULL
1231 *
1232 * Remove an entry with two strings as key.
1233 *
1234 * See xmlHashRemoveEntry.
1235 *
1236 * Returns 0 on success and -1 in case of error.
1237 */
1238 int
xmlHashRemoveEntry2(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,xmlHashDeallocator dealloc)1239 xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
1240 const xmlChar *key2, xmlHashDeallocator dealloc) {
1241 return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
1242 }
1243
1244 /**
1245 * xmlHashRemoveEntry3:
1246 * @hash: hash table
1247 * @key: first string key
1248 * @key2: second string key
1249 * @key3: third string key
1250 * @dealloc: deallocator function for removed item or NULL
1251 *
1252 * Remove an entry with three strings as key.
1253 *
1254 * See xmlHashRemoveEntry.
1255 *
1256 * Returns 0 on success and -1 in case of error.
1257 */
1258 ATTRIBUTE_NO_SANITIZE_INTEGER
1259 int
xmlHashRemoveEntry3(xmlHashTablePtr hash,const xmlChar * key,const xmlChar * key2,const xmlChar * key3,xmlHashDeallocator dealloc)1260 xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
1261 const xmlChar *key2, const xmlChar *key3,
1262 xmlHashDeallocator dealloc) {
1263 xmlHashEntry *entry, *cur, *next;
1264 unsigned hashValue, mask, pos, nextpos;
1265 int found;
1266
1267 if ((hash == NULL) || (hash->size == 0) || (key == NULL))
1268 return(-1);
1269
1270 hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
1271 entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
1272 if (!found)
1273 return(-1);
1274
1275 if ((dealloc != NULL) && (entry->payload != NULL))
1276 dealloc(entry->payload, entry->key);
1277 if (hash->dict == NULL) {
1278 if (entry->key)
1279 xmlFree(entry->key);
1280 if (entry->key2)
1281 xmlFree(entry->key2);
1282 if (entry->key3)
1283 xmlFree(entry->key3);
1284 }
1285
1286 /*
1287 * Find end of probe sequence. Entries at their initial probe
1288 * position start a new sequence.
1289 */
1290 mask = hash->size - 1;
1291 pos = entry - hash->table;
1292 cur = entry;
1293
1294 while (1) {
1295 nextpos = pos + 1;
1296 next = cur + 1;
1297 if ((nextpos & mask) == 0)
1298 next = hash->table;
1299
1300 if ((next->hashValue == 0) ||
1301 (((next->hashValue - nextpos) & mask) == 0))
1302 break;
1303
1304 cur = next;
1305 pos = nextpos;
1306 }
1307
1308 /*
1309 * Backward shift
1310 */
1311 next = entry + 1;
1312
1313 if (cur < entry) {
1314 xmlHashEntry *end = &hash->table[hash->size];
1315
1316 memmove(entry, next, (char *) end - (char *) next);
1317 entry = hash->table;
1318 end[-1] = *entry;
1319 next = entry + 1;
1320 }
1321
1322 memmove(entry, next, (char *) cur - (char *) entry);
1323
1324 /*
1325 * Update entry
1326 */
1327 cur->hashValue = 0;
1328
1329 hash->nbElems--;
1330
1331 return(0);
1332 }
1333
1334