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