xref: /aosp_15_r20/external/cronet/third_party/libxml/src/dict.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /*
2  * dict.c: dictionary of reusable strings, just used to avoid allocation
3  *         and freeing operations.
4  *
5  * Copyright (C) 2003-2012 Daniel Veillard.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14  * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15  *
16  * Author: [email protected]
17  */
18 
19 #define IN_LIBXML
20 #include "libxml.h"
21 
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 
28 #ifdef _WIN32
29 #define WIN32_LEAN_AND_MEAN
30 #include <windows.h>
31 #endif
32 
33 #include "private/dict.h"
34 #include "private/globals.h"
35 #include "private/threads.h"
36 
37 #include <libxml/parser.h>
38 #include <libxml/dict.h>
39 #include <libxml/xmlmemory.h>
40 #include <libxml/xmlstring.h>
41 
42 #ifndef SIZE_MAX
43   #define SIZE_MAX ((size_t) -1)
44 #endif
45 
46 #define MAX_FILL_NUM 7
47 #define MAX_FILL_DENOM 8
48 #define MIN_HASH_SIZE 8
49 #define MAX_HASH_SIZE (1u << 31)
50 
51 typedef struct _xmlDictStrings xmlDictStrings;
52 typedef xmlDictStrings *xmlDictStringsPtr;
53 struct _xmlDictStrings {
54     xmlDictStringsPtr next;
55     xmlChar *free;
56     xmlChar *end;
57     size_t size;
58     size_t nbStrings;
59     xmlChar array[1];
60 };
61 
62 typedef xmlHashedString xmlDictEntry;
63 
64 /*
65  * The entire dictionary
66  */
67 struct _xmlDict {
68     int ref_counter;
69 
70     xmlDictEntry *table;
71     size_t size;
72     unsigned int nbElems;
73     xmlDictStringsPtr strings;
74 
75     struct _xmlDict *subdict;
76     /* used for randomization */
77     unsigned seed;
78     /* used to impose a limit on size */
79     size_t limit;
80 };
81 
82 /*
83  * A mutex for modifying the reference counter for shared
84  * dictionaries.
85  */
86 static xmlMutex xmlDictMutex;
87 
88 /**
89  * xmlInitializeDict:
90  *
91  * DEPRECATED: Alias for xmlInitParser.
92  *
93  * Returns 0.
94  */
95 int
xmlInitializeDict(void)96 xmlInitializeDict(void) {
97     xmlInitParser();
98     return(0);
99 }
100 
101 /**
102  * xmlInitDictInternal:
103  *
104  * Initialize mutex.
105  */
106 void
xmlInitDictInternal(void)107 xmlInitDictInternal(void) {
108     xmlInitMutex(&xmlDictMutex);
109 }
110 
111 /**
112  * xmlDictCleanup:
113  *
114  * DEPRECATED: This function is a no-op. Call xmlCleanupParser
115  * to free global state but see the warnings there. xmlCleanupParser
116  * should be only called once at program exit. In most cases, you don't
117  * have call cleanup functions at all.
118  */
119 void
xmlDictCleanup(void)120 xmlDictCleanup(void) {
121 }
122 
123 /**
124  * xmlCleanupDictInternal:
125  *
126  * Free the dictionary mutex.
127  */
128 void
xmlCleanupDictInternal(void)129 xmlCleanupDictInternal(void) {
130     xmlCleanupMutex(&xmlDictMutex);
131 }
132 
133 /*
134  * xmlDictAddString:
135  * @dict: the dictionary
136  * @name: the name of the userdata
137  * @len: the length of the name
138  *
139  * Add the string to the array[s]
140  *
141  * Returns the pointer of the local string, or NULL in case of error.
142  */
143 static const xmlChar *
xmlDictAddString(xmlDictPtr dict,const xmlChar * name,unsigned int namelen)144 xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
145     xmlDictStringsPtr pool;
146     const xmlChar *ret;
147     size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
148     size_t limit = 0;
149 
150     pool = dict->strings;
151     while (pool != NULL) {
152 	if ((size_t)(pool->end - pool->free) > namelen)
153 	    goto found_pool;
154 	if (pool->size > size) size = pool->size;
155         limit += pool->size;
156 	pool = pool->next;
157     }
158     /*
159      * Not found, need to allocate
160      */
161     if (pool == NULL) {
162         if ((dict->limit > 0) && (limit > dict->limit)) {
163             return(NULL);
164         }
165 
166         if (size == 0) {
167             size = 1000;
168         } else {
169             if (size < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
170                 size *= 4; /* exponential growth */
171             else
172                 size = SIZE_MAX - sizeof(xmlDictStrings);
173         }
174         if (size / 4 < namelen) {
175             if ((size_t) namelen + 0 < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
176                 size = 4 * (size_t) namelen; /* just in case ! */
177             else
178                 return(NULL);
179         }
180 	pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
181 	if (pool == NULL)
182 	    return(NULL);
183 	pool->size = size;
184 	pool->nbStrings = 0;
185 	pool->free = &pool->array[0];
186 	pool->end = &pool->array[size];
187 	pool->next = dict->strings;
188 	dict->strings = pool;
189     }
190 found_pool:
191     ret = pool->free;
192     memcpy(pool->free, name, namelen);
193     pool->free += namelen;
194     *(pool->free++) = 0;
195     pool->nbStrings++;
196     return(ret);
197 }
198 
199 /*
200  * xmlDictAddQString:
201  * @dict: the dictionary
202  * @prefix: the prefix of the userdata
203  * @plen: the prefix length
204  * @name: the name of the userdata
205  * @len: the length of the name
206  *
207  * Add the QName to the array[s]
208  *
209  * Returns the pointer of the local string, or NULL in case of error.
210  */
211 static const xmlChar *
xmlDictAddQString(xmlDictPtr dict,const xmlChar * prefix,unsigned int plen,const xmlChar * name,unsigned int namelen)212 xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
213                  const xmlChar *name, unsigned int namelen)
214 {
215     xmlDictStringsPtr pool;
216     const xmlChar *ret;
217     size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
218     size_t limit = 0;
219 
220     pool = dict->strings;
221     while (pool != NULL) {
222 	if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
223 	    goto found_pool;
224 	if (pool->size > size) size = pool->size;
225         limit += pool->size;
226 	pool = pool->next;
227     }
228     /*
229      * Not found, need to allocate
230      */
231     if (pool == NULL) {
232         if ((dict->limit > 0) && (limit > dict->limit)) {
233             return(NULL);
234         }
235 
236         if (size == 0) size = 1000;
237 	else size *= 4; /* exponential growth */
238         if (size < 4 * (namelen + plen + 1))
239 	    size = 4 * (namelen + plen + 1); /* just in case ! */
240 	pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
241 	if (pool == NULL)
242 	    return(NULL);
243 	pool->size = size;
244 	pool->nbStrings = 0;
245 	pool->free = &pool->array[0];
246 	pool->end = &pool->array[size];
247 	pool->next = dict->strings;
248 	dict->strings = pool;
249     }
250 found_pool:
251     ret = pool->free;
252     memcpy(pool->free, prefix, plen);
253     pool->free += plen;
254     *(pool->free++) = ':';
255     memcpy(pool->free, name, namelen);
256     pool->free += namelen;
257     *(pool->free++) = 0;
258     pool->nbStrings++;
259     return(ret);
260 }
261 
262 /**
263  * xmlDictCreate:
264  *
265  * Create a new dictionary
266  *
267  * Returns the newly created dictionary, or NULL if an error occurred.
268  */
269 xmlDictPtr
xmlDictCreate(void)270 xmlDictCreate(void) {
271     xmlDictPtr dict;
272 
273     xmlInitParser();
274 
275     dict = xmlMalloc(sizeof(xmlDict));
276     if (dict == NULL)
277         return(NULL);
278     dict->ref_counter = 1;
279     dict->limit = 0;
280 
281     dict->size = 0;
282     dict->nbElems = 0;
283     dict->table = NULL;
284     dict->strings = NULL;
285     dict->subdict = NULL;
286     dict->seed = xmlRandom();
287 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
288     dict->seed = 0;
289 #endif
290     return(dict);
291 }
292 
293 /**
294  * xmlDictCreateSub:
295  * @sub: an existing dictionary
296  *
297  * Create a new dictionary, inheriting strings from the read-only
298  * dictionary @sub. On lookup, strings are first searched in the
299  * new dictionary, then in @sub, and if not found are created in the
300  * new dictionary.
301  *
302  * Returns the newly created dictionary, or NULL if an error occurred.
303  */
304 xmlDictPtr
xmlDictCreateSub(xmlDictPtr sub)305 xmlDictCreateSub(xmlDictPtr sub) {
306     xmlDictPtr dict = xmlDictCreate();
307 
308     if ((dict != NULL) && (sub != NULL)) {
309         dict->seed = sub->seed;
310         dict->subdict = sub;
311 	xmlDictReference(dict->subdict);
312     }
313     return(dict);
314 }
315 
316 /**
317  * xmlDictReference:
318  * @dict: the dictionary
319  *
320  * Increment the reference counter of a dictionary
321  *
322  * Returns 0 in case of success and -1 in case of error
323  */
324 int
xmlDictReference(xmlDictPtr dict)325 xmlDictReference(xmlDictPtr dict) {
326     if (dict == NULL) return -1;
327     xmlMutexLock(&xmlDictMutex);
328     dict->ref_counter++;
329     xmlMutexUnlock(&xmlDictMutex);
330     return(0);
331 }
332 
333 /**
334  * xmlDictFree:
335  * @dict: the dictionary
336  *
337  * Free the hash @dict and its contents. The userdata is
338  * deallocated with @f if provided.
339  */
340 void
xmlDictFree(xmlDictPtr dict)341 xmlDictFree(xmlDictPtr dict) {
342     xmlDictStringsPtr pool, nextp;
343 
344     if (dict == NULL)
345 	return;
346 
347     /* decrement the counter, it may be shared by a parser and docs */
348     xmlMutexLock(&xmlDictMutex);
349     dict->ref_counter--;
350     if (dict->ref_counter > 0) {
351         xmlMutexUnlock(&xmlDictMutex);
352         return;
353     }
354 
355     xmlMutexUnlock(&xmlDictMutex);
356 
357     if (dict->subdict != NULL) {
358         xmlDictFree(dict->subdict);
359     }
360 
361     if (dict->table) {
362 	xmlFree(dict->table);
363     }
364     pool = dict->strings;
365     while (pool != NULL) {
366         nextp = pool->next;
367 	xmlFree(pool);
368 	pool = nextp;
369     }
370     xmlFree(dict);
371 }
372 
373 /**
374  * xmlDictOwns:
375  * @dict: the dictionary
376  * @str: the string
377  *
378  * check if a string is owned by the dictionary
379  *
380  * Returns 1 if true, 0 if false and -1 in case of error
381  * -1 in case of error
382  */
383 int
xmlDictOwns(xmlDictPtr dict,const xmlChar * str)384 xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
385     xmlDictStringsPtr pool;
386 
387     if ((dict == NULL) || (str == NULL))
388 	return(-1);
389     pool = dict->strings;
390     while (pool != NULL) {
391         if ((str >= &pool->array[0]) && (str <= pool->free))
392 	    return(1);
393 	pool = pool->next;
394     }
395     if (dict->subdict)
396         return(xmlDictOwns(dict->subdict, str));
397     return(0);
398 }
399 
400 /**
401  * xmlDictSize:
402  * @dict: the dictionary
403  *
404  * Query the number of elements installed in the hash @dict.
405  *
406  * Returns the number of elements in the dictionary or
407  * -1 in case of error
408  */
409 int
xmlDictSize(xmlDictPtr dict)410 xmlDictSize(xmlDictPtr dict) {
411     if (dict == NULL)
412 	return(-1);
413     if (dict->subdict)
414         return(dict->nbElems + dict->subdict->nbElems);
415     return(dict->nbElems);
416 }
417 
418 /**
419  * xmlDictSetLimit:
420  * @dict: the dictionary
421  * @limit: the limit in bytes
422  *
423  * Set a size limit for the dictionary
424  * Added in 2.9.0
425  *
426  * Returns the previous limit of the dictionary or 0
427  */
428 size_t
xmlDictSetLimit(xmlDictPtr dict,size_t limit)429 xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
430     size_t ret;
431 
432     if (dict == NULL)
433 	return(0);
434     ret = dict->limit;
435     dict->limit = limit;
436     return(ret);
437 }
438 
439 /**
440  * xmlDictGetUsage:
441  * @dict: the dictionary
442  *
443  * Get how much memory is used by a dictionary for strings
444  * Added in 2.9.0
445  *
446  * Returns the amount of strings allocated
447  */
448 size_t
xmlDictGetUsage(xmlDictPtr dict)449 xmlDictGetUsage(xmlDictPtr dict) {
450     xmlDictStringsPtr pool;
451     size_t limit = 0;
452 
453     if (dict == NULL)
454 	return(0);
455     pool = dict->strings;
456     while (pool != NULL) {
457         limit += pool->size;
458 	pool = pool->next;
459     }
460     return(limit);
461 }
462 
463 /*****************************************************************
464  *
465  * The code below was rewritten and is additionally licensed under
466  * the main license in file 'Copyright'.
467  *
468  *****************************************************************/
469 
470 ATTRIBUTE_NO_SANITIZE_INTEGER
471 static unsigned
xmlDictHashName(unsigned seed,const xmlChar * data,size_t maxLen,size_t * plen)472 xmlDictHashName(unsigned seed, const xmlChar* data, size_t maxLen,
473                 size_t *plen) {
474     unsigned h1, h2;
475     size_t i;
476 
477     HASH_INIT(h1, h2, seed);
478 
479     for (i = 0; i < maxLen && data[i]; i++) {
480         HASH_UPDATE(h1, h2, data[i]);
481     }
482 
483     HASH_FINISH(h1, h2);
484 
485     *plen = i;
486     return(h2 | MAX_HASH_SIZE);
487 }
488 
489 ATTRIBUTE_NO_SANITIZE_INTEGER
490 static unsigned
xmlDictHashQName(unsigned seed,const xmlChar * prefix,const xmlChar * name,size_t * pplen,size_t * plen)491 xmlDictHashQName(unsigned seed, const xmlChar *prefix, const xmlChar *name,
492                  size_t *pplen, size_t *plen) {
493     unsigned h1, h2;
494     size_t i;
495 
496     HASH_INIT(h1, h2, seed);
497 
498     for (i = 0; prefix[i] != 0; i++) {
499         HASH_UPDATE(h1, h2, prefix[i]);
500     }
501     *pplen = i;
502 
503     HASH_UPDATE(h1, h2, ':');
504 
505     for (i = 0; name[i] != 0; i++) {
506         HASH_UPDATE(h1, h2, name[i]);
507     }
508     *plen = i;
509 
510     HASH_FINISH(h1, h2);
511 
512     /*
513      * Always set the upper bit of hash values since 0 means an unoccupied
514      * bucket.
515      */
516     return(h2 | MAX_HASH_SIZE);
517 }
518 
519 unsigned
xmlDictComputeHash(const xmlDict * dict,const xmlChar * string)520 xmlDictComputeHash(const xmlDict *dict, const xmlChar *string) {
521     size_t len;
522     return(xmlDictHashName(dict->seed, string, SIZE_MAX, &len));
523 }
524 
525 #define HASH_ROL31(x,n) ((x) << (n) | ((x) & 0x7FFFFFFF) >> (31 - (n)))
526 
527 ATTRIBUTE_NO_SANITIZE_INTEGER
528 unsigned
xmlDictCombineHash(unsigned v1,unsigned v2)529 xmlDictCombineHash(unsigned v1, unsigned v2) {
530     /*
531      * The upper bit of hash values is always set, so we have to operate on
532      * 31-bit hashes here.
533      */
534     v1 ^= v2;
535     v1 += HASH_ROL31(v2, 5);
536 
537     return((v1 & 0xFFFFFFFF) | 0x80000000);
538 }
539 
540 /**
541  * xmlDictFindEntry:
542  * @dict: dict
543  * @prefix: optional QName prefix
544  * @name: string
545  * @len: length of string
546  * @hashValue: valid hash value of string
547  * @pfound: result of search
548  *
549  * Try to find a matching hash table entry. If an entry was found, set
550  * @found to 1 and return the entry. Otherwise, set @found to 0 and return
551  * the location where a new entry should be inserted.
552  */
553 ATTRIBUTE_NO_SANITIZE_INTEGER
554 static xmlDictEntry *
xmlDictFindEntry(const xmlDict * dict,const xmlChar * prefix,const xmlChar * name,int len,unsigned hashValue,int * pfound)555 xmlDictFindEntry(const xmlDict *dict, const xmlChar *prefix,
556                  const xmlChar *name, int len, unsigned hashValue,
557                  int *pfound) {
558     xmlDictEntry *entry;
559     unsigned mask, pos, displ;
560     int found = 0;
561 
562     mask = dict->size - 1;
563     pos = hashValue & mask;
564     entry = &dict->table[pos];
565 
566     if (entry->hashValue != 0) {
567         /*
568          * Robin hood hashing: abort if the displacement of the entry
569          * is smaller than the displacement of the key we look for.
570          * This also stops at the correct position when inserting.
571          */
572         displ = 0;
573 
574         do {
575             if (entry->hashValue == hashValue) {
576                 if (prefix == NULL) {
577                     /*
578                      * name is not necessarily null-terminated.
579                      */
580                     if ((strncmp((const char *) entry->name,
581                                  (const char *) name, len) == 0) &&
582                         (entry->name[len] == 0)) {
583                         found = 1;
584                         break;
585                     }
586                 } else {
587                     if (xmlStrQEqual(prefix, name, entry->name)) {
588                         found = 1;
589                         break;
590                     }
591                 }
592             }
593 
594             displ++;
595             pos++;
596             entry++;
597             if ((pos & mask) == 0)
598                 entry = dict->table;
599         } while ((entry->hashValue != 0) &&
600                  (((pos - entry->hashValue) & mask) >= displ));
601     }
602 
603     *pfound = found;
604     return(entry);
605 }
606 
607 /**
608  * xmlDictGrow:
609  * @dict: dictionary
610  * @size: new size of the dictionary
611  *
612  * Resize the dictionary hash table.
613  *
614  * Returns 0 in case of success, -1 if a memory allocation failed.
615  */
616 static int
xmlDictGrow(xmlDictPtr dict,unsigned size)617 xmlDictGrow(xmlDictPtr dict, unsigned size) {
618     const xmlDictEntry *oldentry, *oldend, *end;
619     xmlDictEntry *table;
620     unsigned oldsize, i;
621 
622     /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
623     if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
624         return(-1);
625     table = xmlMalloc(size * sizeof(table[0]));
626     if (table == NULL)
627         return(-1);
628     memset(table, 0, size * sizeof(table[0]));
629 
630     oldsize = dict->size;
631     if (oldsize == 0)
632         goto done;
633 
634     oldend = &dict->table[oldsize];
635     end = &table[size];
636 
637     /*
638      * Robin Hood sorting order is maintained if we
639      *
640      * - compute dict indices with modulo
641      * - resize by an integer factor
642      * - start to copy from the beginning of a probe sequence
643      */
644     oldentry = dict->table;
645     while (oldentry->hashValue != 0) {
646         if (++oldentry >= oldend)
647             oldentry = dict->table;
648     }
649 
650     for (i = 0; i < oldsize; i++) {
651         if (oldentry->hashValue != 0) {
652             xmlDictEntry *entry = &table[oldentry->hashValue & (size - 1)];
653 
654             while (entry->hashValue != 0) {
655                 if (++entry >= end)
656                     entry = table;
657             }
658             *entry = *oldentry;
659         }
660 
661         if (++oldentry >= oldend)
662             oldentry = dict->table;
663     }
664 
665     xmlFree(dict->table);
666 
667 done:
668     dict->table = table;
669     dict->size = size;
670 
671     return(0);
672 }
673 
674 /**
675  * xmlDictLookupInternal:
676  * @dict: dict
677  * @prefix: optional QName prefix
678  * @name: string
679  * @maybeLen: length of string or -1 if unknown
680  * @update: whether the string should be added
681  *
682  * Internal lookup and update function.
683  */
684 ATTRIBUTE_NO_SANITIZE_INTEGER
685 static const xmlDictEntry *
xmlDictLookupInternal(xmlDictPtr dict,const xmlChar * prefix,const xmlChar * name,int maybeLen,int update)686 xmlDictLookupInternal(xmlDictPtr dict, const xmlChar *prefix,
687                       const xmlChar *name, int maybeLen, int update) {
688     xmlDictEntry *entry = NULL;
689     const xmlChar *ret;
690     unsigned hashValue;
691     size_t maxLen, len, plen, klen;
692     int found = 0;
693 
694     if ((dict == NULL) || (name == NULL))
695 	return(NULL);
696 
697     maxLen = (maybeLen < 0) ? SIZE_MAX : (size_t) maybeLen;
698 
699     if (prefix == NULL) {
700         hashValue = xmlDictHashName(dict->seed, name, maxLen, &len);
701         if (len > INT_MAX / 2)
702             return(NULL);
703         klen = len;
704     } else {
705         hashValue = xmlDictHashQName(dict->seed, prefix, name, &plen, &len);
706         if ((len > INT_MAX / 2) || (plen >= INT_MAX / 2 - len))
707             return(NULL);
708         klen = plen + 1 + len;
709     }
710 
711     if ((dict->limit > 0) && (klen >= dict->limit))
712         return(NULL);
713 
714     /*
715      * Check for an existing entry
716      */
717     if (dict->size > 0)
718         entry = xmlDictFindEntry(dict, prefix, name, klen, hashValue, &found);
719     if (found)
720         return(entry);
721 
722     if ((dict->subdict != NULL) && (dict->subdict->size > 0)) {
723         xmlDictEntry *subEntry;
724         unsigned subHashValue;
725 
726         if (prefix == NULL)
727             subHashValue = xmlDictHashName(dict->subdict->seed, name, len,
728                                            &len);
729         else
730             subHashValue = xmlDictHashQName(dict->subdict->seed, prefix, name,
731                                             &plen, &len);
732         subEntry = xmlDictFindEntry(dict->subdict, prefix, name, klen,
733                                     subHashValue, &found);
734         if (found)
735             return(subEntry);
736     }
737 
738     if (!update)
739         return(NULL);
740 
741     /*
742      * Grow the hash table if needed
743      */
744     if (dict->nbElems + 1 > dict->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
745         unsigned newSize, mask, displ, pos;
746 
747         if (dict->size == 0) {
748             newSize = MIN_HASH_SIZE;
749         } else {
750             if (dict->size >= MAX_HASH_SIZE)
751                 return(NULL);
752             newSize = dict->size * 2;
753         }
754         if (xmlDictGrow(dict, newSize) != 0)
755             return(NULL);
756 
757         /*
758          * Find new entry
759          */
760         mask = dict->size - 1;
761         displ = 0;
762         pos = hashValue & mask;
763         entry = &dict->table[pos];
764 
765         while ((entry->hashValue != 0) &&
766                ((pos - entry->hashValue) & mask) >= displ) {
767             displ++;
768             pos++;
769             entry++;
770             if ((pos & mask) == 0)
771                 entry = dict->table;
772         }
773     }
774 
775     if (prefix == NULL)
776         ret = xmlDictAddString(dict, name, len);
777     else
778         ret = xmlDictAddQString(dict, prefix, plen, name, len);
779     if (ret == NULL)
780         return(NULL);
781 
782     /*
783      * Shift the remainder of the probe sequence to the right
784      */
785     if (entry->hashValue != 0) {
786         const xmlDictEntry *end = &dict->table[dict->size];
787         const xmlDictEntry *cur = entry;
788 
789         do {
790             cur++;
791             if (cur >= end)
792                 cur = dict->table;
793         } while (cur->hashValue != 0);
794 
795         if (cur < entry) {
796             /*
797              * If we traversed the end of the buffer, handle the part
798              * at the start of the buffer.
799              */
800             memmove(&dict->table[1], dict->table,
801                     (char *) cur - (char *) dict->table);
802             cur = end - 1;
803             dict->table[0] = *cur;
804         }
805 
806         memmove(&entry[1], entry, (char *) cur - (char *) entry);
807     }
808 
809     /*
810      * Populate entry
811      */
812     entry->hashValue = hashValue;
813     entry->name = ret;
814 
815     dict->nbElems++;
816 
817     return(entry);
818 }
819 
820 /**
821  * xmlDictLookup:
822  * @dict: dictionary
823  * @name: string key
824  * @len: length of the key, if -1 it is recomputed
825  *
826  * Lookup a string and add it to the dictionary if it wasn't found.
827  *
828  * Returns the interned copy of the string or NULL if a memory allocation
829  * failed.
830  */
831 const xmlChar *
xmlDictLookup(xmlDictPtr dict,const xmlChar * name,int len)832 xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
833     const xmlDictEntry *entry;
834 
835     entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
836     if (entry == NULL)
837         return(NULL);
838     return(entry->name);
839 }
840 
841 /**
842  * xmlDictLookupHashed:
843  * @dict: dictionary
844  * @name: string key
845  * @len: length of the key, if -1 it is recomputed
846  *
847  * Lookup a dictionary entry and add the string to the dictionary if
848  * it wasn't found.
849  *
850  * Returns the dictionary entry.
851  */
852 xmlHashedString
xmlDictLookupHashed(xmlDictPtr dict,const xmlChar * name,int len)853 xmlDictLookupHashed(xmlDictPtr dict, const xmlChar *name, int len) {
854     const xmlDictEntry *entry;
855     xmlHashedString ret;
856 
857     entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
858 
859     if (entry == NULL) {
860         ret.name = NULL;
861         ret.hashValue = 0;
862     } else {
863         ret = *entry;
864     }
865 
866     return(ret);
867 }
868 
869 /**
870  * xmlDictExists:
871  * @dict: the dictionary
872  * @name: the name of the userdata
873  * @len: the length of the name, if -1 it is recomputed
874  *
875  * Check if a string exists in the dictionary.
876  *
877  * Returns the internal copy of the name or NULL if not found.
878  */
879 const xmlChar *
xmlDictExists(xmlDictPtr dict,const xmlChar * name,int len)880 xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
881     const xmlDictEntry *entry;
882 
883     entry = xmlDictLookupInternal(dict, NULL, name, len, 0);
884     if (entry == NULL)
885         return(NULL);
886     return(entry->name);
887 }
888 
889 /**
890  * xmlDictQLookup:
891  * @dict: the dictionary
892  * @prefix: the prefix
893  * @name: the name
894  *
895  * Lookup the QName @prefix:@name and add it to the dictionary if
896  * it wasn't found.
897  *
898  * Returns the interned copy of the string or NULL if a memory allocation
899  * failed.
900  */
901 const xmlChar *
xmlDictQLookup(xmlDictPtr dict,const xmlChar * prefix,const xmlChar * name)902 xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
903     const xmlDictEntry *entry;
904 
905     entry = xmlDictLookupInternal(dict, prefix, name, -1, 1);
906     if (entry == NULL)
907         return(NULL);
908     return(entry->name);
909 }
910 
911 /*
912  * Pseudo-random generator
913  */
914 
915 static xmlMutex xmlRngMutex;
916 
917 static unsigned globalRngState[2];
918 
919 ATTRIBUTE_NO_SANITIZE_INTEGER
920 void
xmlInitRandom(void)921 xmlInitRandom(void) {
922     xmlInitMutex(&xmlRngMutex);
923 
924     {
925         int var;
926 
927         globalRngState[0] =
928                 (unsigned) time(NULL) ^
929                 HASH_ROL((unsigned) ((size_t) &xmlInitRandom & 0xFFFFFFFF), 8);
930         globalRngState[1] =
931                 HASH_ROL((unsigned) ((size_t) &xmlRngMutex & 0xFFFFFFFF), 16) ^
932                 HASH_ROL((unsigned) ((size_t) &var & 0xFFFFFFFF), 24);
933     }
934 }
935 
936 void
xmlCleanupRandom(void)937 xmlCleanupRandom(void) {
938     xmlCleanupMutex(&xmlRngMutex);
939 }
940 
941 ATTRIBUTE_NO_SANITIZE_INTEGER
942 static unsigned
xoroshiro64ss(unsigned * s)943 xoroshiro64ss(unsigned *s) {
944     unsigned s0 = s[0];
945     unsigned s1 = s[1];
946     unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5;
947 
948     s1 ^= s0;
949     s[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9);
950     s[1] = HASH_ROL(s1, 13);
951 
952     return(result & 0xFFFFFFFF);
953 }
954 
955 unsigned
xmlGlobalRandom(void)956 xmlGlobalRandom(void) {
957     unsigned ret;
958 
959     xmlMutexLock(&xmlRngMutex);
960     ret = xoroshiro64ss(globalRngState);
961     xmlMutexUnlock(&xmlRngMutex);
962 
963     return(ret);
964 }
965 
966 unsigned
xmlRandom(void)967 xmlRandom(void) {
968 #ifdef LIBXML_THREAD_ENABLED
969     return(xoroshiro64ss(xmlGetLocalRngState()));
970 #else
971     return(xmlGlobalRandom());
972 #endif
973 }
974 
975