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