Coverage Report

Created: 2024-02-04 06:18

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