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