Coverage Report

Created: 2024-02-04 06:19

/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
1.47M
  #define SIZE_MAX ((size_t) -1)
48
#endif
49
50
849k
#define MAX_FILL_NUM 7
51
849k
#define MAX_FILL_DENOM 8
52
85.2k
#define MIN_HASH_SIZE 8
53
10.2M
#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
838k
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
149
838k
    xmlDictStringsPtr pool;
150
838k
    const xmlChar *ret;
151
838k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
152
838k
    size_t limit = 0;
153
154
838k
    pool = dict->strings;
155
842k
    while (pool != NULL) {
156
753k
  if ((size_t)(pool->end - pool->free) > namelen)
157
749k
      goto found_pool;
158
4.34k
  if (pool->size > size) size = pool->size;
159
4.34k
        limit += pool->size;
160
4.34k
  pool = pool->next;
161
4.34k
    }
162
    /*
163
     * Not found, need to allocate
164
     */
165
88.6k
    if (pool == NULL) {
166
88.6k
        if ((dict->limit > 0) && (limit > dict->limit)) {
167
4
            return(NULL);
168
4
        }
169
170
88.6k
        if (size == 0) {
171
85.3k
            size = 1000;
172
85.3k
        } else {
173
3.32k
            if (size < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
174
3.32k
                size *= 4; /* exponential growth */
175
0
            else
176
0
                size = SIZE_MAX - sizeof(xmlDictStrings);
177
3.32k
        }
178
88.6k
        if (size / 4 < namelen) {
179
1.25k
            if ((size_t) namelen + 0 < (SIZE_MAX - sizeof(xmlDictStrings)) / 4)
180
1.25k
                size = 4 * (size_t) namelen; /* just in case ! */
181
0
            else
182
0
                return(NULL);
183
1.25k
        }
184
88.6k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
185
88.6k
  if (pool == NULL)
186
401
      return(NULL);
187
88.2k
  pool->size = size;
188
88.2k
  pool->nbStrings = 0;
189
88.2k
  pool->free = &pool->array[0];
190
88.2k
  pool->end = &pool->array[size];
191
88.2k
  pool->next = dict->strings;
192
88.2k
  dict->strings = pool;
193
88.2k
    }
194
837k
found_pool:
195
837k
    ret = pool->free;
196
837k
    memcpy(pool->free, name, namelen);
197
837k
    pool->free += namelen;
198
837k
    *(pool->free++) = 0;
199
837k
    pool->nbStrings++;
200
837k
    return(ret);
201
88.6k
}
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
10.6k
{
219
10.6k
    xmlDictStringsPtr pool;
220
10.6k
    const xmlChar *ret;
221
10.6k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
222
10.6k
    size_t limit = 0;
223
224
10.6k
    pool = dict->strings;
225
10.8k
    while (pool != NULL) {
226
10.7k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
227
10.5k
      goto found_pool;
228
225
  if (pool->size > size) size = pool->size;
229
225
        limit += pool->size;
230
225
  pool = pool->next;
231
225
    }
232
    /*
233
     * Not found, need to allocate
234
     */
235
97
    if (pool == NULL) {
236
97
        if ((dict->limit > 0) && (limit > dict->limit)) {
237
0
            return(NULL);
238
0
        }
239
240
97
        if (size == 0) size = 1000;
241
97
  else size *= 4; /* exponential growth */
242
97
        if (size < 4 * (namelen + plen + 1))
243
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
244
97
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
245
97
  if (pool == NULL)
246
6
      return(NULL);
247
91
  pool->size = size;
248
91
  pool->nbStrings = 0;
249
91
  pool->free = &pool->array[0];
250
91
  pool->end = &pool->array[size];
251
91
  pool->next = dict->strings;
252
91
  dict->strings = pool;
253
91
    }
254
10.6k
found_pool:
255
10.6k
    ret = pool->free;
256
10.6k
    memcpy(pool->free, prefix, plen);
257
10.6k
    pool->free += plen;
258
10.6k
    *(pool->free++) = ':';
259
10.6k
    memcpy(pool->free, name, namelen);
260
10.6k
    pool->free += namelen;
261
10.6k
    *(pool->free++) = 0;
262
10.6k
    pool->nbStrings++;
263
10.6k
    return(ret);
264
97
}
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
86.4k
xmlDictCreate(void) {
275
86.4k
    xmlDictPtr dict;
276
277
86.4k
    xmlInitParser();
278
279
86.4k
    dict = xmlMalloc(sizeof(xmlDict));
280
86.4k
    if (dict == NULL)
281
14
        return(NULL);
282
86.4k
    dict->ref_counter = 1;
283
86.4k
    dict->limit = 0;
284
285
86.4k
    dict->size = 0;
286
86.4k
    dict->nbElems = 0;
287
86.4k
    dict->table = NULL;
288
86.4k
    dict->strings = NULL;
289
86.4k
    dict->subdict = NULL;
290
86.4k
    dict->seed = xmlRandom();
291
86.4k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
292
86.4k
    dict->seed = 0;
293
86.4k
#endif
294
86.4k
    return(dict);
295
86.4k
}
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
132k
xmlDictReference(xmlDictPtr dict) {
330
132k
    if (dict == NULL) return -1;
331
110k
    xmlMutexLock(&xmlDictMutex);
332
110k
    dict->ref_counter++;
333
110k
    xmlMutexUnlock(&xmlDictMutex);
334
110k
    return(0);
335
132k
}
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
197k
xmlDictFree(xmlDictPtr dict) {
346
197k
    xmlDictStringsPtr pool, nextp;
347
348
197k
    if (dict == NULL)
349
0
  return;
350
351
    /* decrement the counter, it may be shared by a parser and docs */
352
197k
    xmlMutexLock(&xmlDictMutex);
353
197k
    dict->ref_counter--;
354
197k
    if (dict->ref_counter > 0) {
355
110k
        xmlMutexUnlock(&xmlDictMutex);
356
110k
        return;
357
110k
    }
358
359
86.4k
    xmlMutexUnlock(&xmlDictMutex);
360
361
86.4k
    if (dict->subdict != NULL) {
362
0
        xmlDictFree(dict->subdict);
363
0
    }
364
365
86.4k
    if (dict->table) {
366
84.9k
  xmlFree(dict->table);
367
84.9k
    }
368
86.4k
    pool = dict->strings;
369
174k
    while (pool != NULL) {
370
88.3k
        nextp = pool->next;
371
88.3k
  xmlFree(pool);
372
88.3k
  pool = nextp;
373
88.3k
    }
374
86.4k
    xmlFree(dict);
375
86.4k
}
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
8.44M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
389
8.44M
    xmlDictStringsPtr pool;
390
391
8.44M
    if ((dict == NULL) || (str == NULL))
392
0
  return(-1);
393
8.44M
    pool = dict->strings;
394
15.7M
    while (pool != NULL) {
395
10.9M
        if ((str >= &pool->array[0]) && (str <= pool->free))
396
3.69M
      return(1);
397
7.27M
  pool = pool->next;
398
7.27M
    }
399
4.75M
    if (dict->subdict)
400
0
        return(xmlDictOwns(dict->subdict, str));
401
4.75M
    return(0);
402
4.75M
}
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
113k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
434
113k
    size_t ret;
435
436
113k
    if (dict == NULL)
437
0
  return(0);
438
113k
    ret = dict->limit;
439
113k
    dict->limit = limit;
440
113k
    return(ret);
441
113k
}
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
10.0M
                size_t *plen) {
478
10.0M
    unsigned h1, h2;
479
10.0M
    size_t i;
480
481
10.0M
    HASH_INIT(h1, h2, seed);
482
483
634M
    for (i = 0; i < maxLen && data[i]; i++) {
484
624M
        HASH_UPDATE(h1, h2, data[i]);
485
624M
    }
486
487
10.0M
    HASH_FINISH(h1, h2);
488
489
10.0M
    *plen = i;
490
10.0M
    return(h2 | MAX_HASH_SIZE);
491
10.0M
}
492
493
ATTRIBUTE_NO_SANITIZE_INTEGER
494
static unsigned
495
xmlDictHashQName(unsigned seed, const xmlChar *prefix, const xmlChar *name,
496
90.9k
                 size_t *pplen, size_t *plen) {
497
90.9k
    unsigned h1, h2;
498
90.9k
    size_t i;
499
500
90.9k
    HASH_INIT(h1, h2, seed);
501
502
933k
    for (i = 0; prefix[i] != 0; i++) {
503
842k
        HASH_UPDATE(h1, h2, prefix[i]);
504
842k
    }
505
90.9k
    *pplen = i;
506
507
90.9k
    HASH_UPDATE(h1, h2, ':');
508
509
1.13M
    for (i = 0; name[i] != 0; i++) {
510
1.04M
        HASH_UPDATE(h1, h2, name[i]);
511
1.04M
    }
512
90.9k
    *plen = i;
513
514
90.9k
    HASH_FINISH(h1, h2);
515
516
    /*
517
     * Always set the upper bit of hash values since 0 means an unoccupied
518
     * bucket.
519
     */
520
90.9k
    return(h2 | MAX_HASH_SIZE);
521
90.9k
}
522
523
unsigned
524
185k
xmlDictComputeHash(const xmlDict *dict, const xmlChar *string) {
525
185k
    size_t len;
526
185k
    return(xmlDictHashName(dict->seed, string, SIZE_MAX, &len));
527
185k
}
528
529
1.09M
#define HASH_ROL31(x,n) ((x) << (n) | ((x) & 0x7FFFFFFF) >> (31 - (n)))
530
531
ATTRIBUTE_NO_SANITIZE_INTEGER
532
unsigned
533
1.09M
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
1.09M
    v1 ^= v2;
539
1.09M
    v1 += HASH_ROL31(v2, 5);
540
541
1.09M
    return((v1 & 0xFFFFFFFF) | 0x80000000);
542
1.09M
}
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
9.89M
                 int *pfound) {
562
9.89M
    xmlDictEntry *entry;
563
9.89M
    unsigned mask, pos, displ;
564
9.89M
    int found = 0;
565
566
9.89M
    mask = dict->size - 1;
567
9.89M
    pos = hashValue & mask;
568
9.89M
    entry = &dict->table[pos];
569
570
9.89M
    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
9.47M
        displ = 0;
577
578
16.5M
        do {
579
16.5M
            if (entry->hashValue == hashValue) {
580
9.12M
                if (prefix == NULL) {
581
                    /*
582
                     * name is not necessarily null-terminated.
583
                     */
584
9.04M
                    if ((strncmp((const char *) entry->name,
585
9.04M
                                 (const char *) name, len) == 0) &&
586
9.04M
                        (entry->name[len] == 0)) {
587
9.04M
                        found = 1;
588
9.04M
                        break;
589
9.04M
                    }
590
9.04M
                } else {
591
80.3k
                    if (xmlStrQEqual(prefix, name, entry->name)) {
592
80.3k
                        found = 1;
593
80.3k
                        break;
594
80.3k
                    }
595
80.3k
                }
596
9.12M
            }
597
598
7.42M
            displ++;
599
7.42M
            pos++;
600
7.42M
            entry++;
601
7.42M
            if ((pos & mask) == 0)
602
374k
                entry = dict->table;
603
7.42M
        } while ((entry->hashValue != 0) &&
604
7.42M
                 (((pos - entry->hashValue) & mask) >= displ));
605
9.47M
    }
606
607
0
    *pfound = found;
608
9.89M
    return(entry);
609
9.89M
}
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
131k
xmlDictGrow(xmlDictPtr dict, unsigned size) {
622
131k
    const xmlDictEntry *oldentry, *oldend, *end;
623
131k
    xmlDictEntry *table;
624
131k
    unsigned oldsize, i;
625
626
    /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
627
131k
    if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
628
0
        return(-1);
629
131k
    table = xmlMalloc(size * sizeof(table[0]));
630
131k
    if (table == NULL)
631
453
        return(-1);
632
131k
    memset(table, 0, size * sizeof(table[0]));
633
634
131k
    oldsize = dict->size;
635
131k
    if (oldsize == 0)
636
84.9k
        goto done;
637
638
46.1k
    oldend = &dict->table[oldsize];
639
46.1k
    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
46.1k
    oldentry = dict->table;
649
305k
    while (oldentry->hashValue != 0) {
650
259k
        if (++oldentry >= oldend)
651
0
            oldentry = dict->table;
652
259k
    }
653
654
767k
    for (i = 0; i < oldsize; i++) {
655
720k
        if (oldentry->hashValue != 0) {
656
630k
            xmlDictEntry *entry = &table[oldentry->hashValue & (size - 1)];
657
658
828k
            while (entry->hashValue != 0) {
659
198k
                if (++entry >= end)
660
15.3k
                    entry = table;
661
198k
            }
662
630k
            *entry = *oldentry;
663
630k
        }
664
665
720k
        if (++oldentry >= oldend)
666
46.1k
            oldentry = dict->table;
667
720k
    }
668
669
46.1k
    xmlFree(dict->table);
670
671
131k
done:
672
131k
    dict->table = table;
673
131k
    dict->size = size;
674
675
131k
    return(0);
676
46.1k
}
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
9.97M
                      const xmlChar *name, int maybeLen, int update) {
692
9.97M
    xmlDictEntry *entry = NULL;
693
9.97M
    const xmlChar *ret;
694
9.97M
    unsigned hashValue;
695
9.97M
    size_t maxLen, len, plen, klen;
696
9.97M
    int found = 0;
697
698
9.97M
    if ((dict == NULL) || (name == NULL))
699
0
  return(NULL);
700
701
9.97M
    maxLen = (maybeLen < 0) ? SIZE_MAX : (size_t) maybeLen;
702
703
9.97M
    if (prefix == NULL) {
704
9.88M
        hashValue = xmlDictHashName(dict->seed, name, maxLen, &len);
705
9.88M
        if (len > INT_MAX / 2)
706
0
            return(NULL);
707
9.88M
        klen = len;
708
9.88M
    } else {
709
90.9k
        hashValue = xmlDictHashQName(dict->seed, prefix, name, &plen, &len);
710
90.9k
        if ((len > INT_MAX / 2) || (plen >= INT_MAX / 2 - len))
711
0
            return(NULL);
712
90.9k
        klen = plen + 1 + len;
713
90.9k
    }
714
715
9.97M
    if ((dict->limit > 0) && (klen >= dict->limit))
716
0
        return(NULL);
717
718
    /*
719
     * Check for an existing entry
720
     */
721
9.97M
    if (dict->size > 0)
722
9.89M
        entry = xmlDictFindEntry(dict, prefix, name, klen, hashValue, &found);
723
9.97M
    if (found)
724
9.12M
        return(entry);
725
726
849k
    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
849k
    if (!update)
743
0
        return(NULL);
744
745
    /*
746
     * Grow the hash table if needed
747
     */
748
849k
    if (dict->nbElems + 1 > dict->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
749
131k
        unsigned newSize, mask, displ, pos;
750
751
131k
        if (dict->size == 0) {
752
85.2k
            newSize = MIN_HASH_SIZE;
753
85.2k
        } else {
754
46.3k
            if (dict->size >= MAX_HASH_SIZE)
755
0
                return(NULL);
756
46.3k
            newSize = dict->size * 2;
757
46.3k
        }
758
131k
        if (xmlDictGrow(dict, newSize) != 0)
759
453
            return(NULL);
760
761
        /*
762
         * Find new entry
763
         */
764
131k
        mask = dict->size - 1;
765
131k
        displ = 0;
766
131k
        pos = hashValue & mask;
767
131k
        entry = &dict->table[pos];
768
769
154k
        while ((entry->hashValue != 0) &&
770
154k
               ((pos - entry->hashValue) & mask) >= displ) {
771
23.3k
            displ++;
772
23.3k
            pos++;
773
23.3k
            entry++;
774
23.3k
            if ((pos & mask) == 0)
775
2.18k
                entry = dict->table;
776
23.3k
        }
777
131k
    }
778
779
848k
    if (prefix == NULL)
780
838k
        ret = xmlDictAddString(dict, name, len);
781
10.6k
    else
782
10.6k
        ret = xmlDictAddQString(dict, prefix, plen, name, len);
783
848k
    if (ret == NULL)
784
411
        return(NULL);
785
786
    /*
787
     * Shift the remainder of the probe sequence to the right
788
     */
789
848k
    if (entry->hashValue != 0) {
790
167k
        const xmlDictEntry *end = &dict->table[dict->size];
791
167k
        const xmlDictEntry *cur = entry;
792
793
716k
        do {
794
716k
            cur++;
795
716k
            if (cur >= end)
796
27.5k
                cur = dict->table;
797
716k
        } while (cur->hashValue != 0);
798
799
167k
        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
27.5k
            memmove(&dict->table[1], dict->table,
805
27.5k
                    (char *) cur - (char *) dict->table);
806
27.5k
            cur = end - 1;
807
27.5k
            dict->table[0] = *cur;
808
27.5k
        }
809
810
167k
        memmove(&entry[1], entry, (char *) cur - (char *) entry);
811
167k
    }
812
813
    /*
814
     * Populate entry
815
     */
816
848k
    entry->hashValue = hashValue;
817
848k
    entry->name = ret;
818
819
848k
    dict->nbElems++;
820
821
848k
    return(entry);
822
848k
}
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
6.96M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
837
6.96M
    const xmlDictEntry *entry;
838
839
6.96M
    entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
840
6.96M
    if (entry == NULL)
841
751
        return(NULL);
842
6.96M
    return(entry->name);
843
6.96M
}
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
2.92M
xmlDictLookupHashed(xmlDictPtr dict, const xmlChar *name, int len) {
858
2.92M
    const xmlDictEntry *entry;
859
2.92M
    xmlHashedString ret;
860
861
2.92M
    entry = xmlDictLookupInternal(dict, NULL, name, len, 1);
862
863
2.92M
    if (entry == NULL) {
864
93
        ret.name = NULL;
865
93
        ret.hashValue = 0;
866
2.92M
    } else {
867
2.92M
        ret = *entry;
868
2.92M
    }
869
870
2.92M
    return(ret);
871
2.92M
}
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
90.9k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
907
90.9k
    const xmlDictEntry *entry;
908
909
90.9k
    entry = xmlDictLookupInternal(dict, prefix, name, -1, 1);
910
90.9k
    if (entry == NULL)
911
20
        return(NULL);
912
90.9k
    return(entry->name);
913
90.9k
}
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
189k
xoroshiro64ss(unsigned *s) {
972
189k
    unsigned s0 = s[0];
973
189k
    unsigned s1 = s[1];
974
189k
    unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5;
975
976
189k
    s1 ^= s0;
977
189k
    s[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9);
978
189k
    s[1] = HASH_ROL(s1, 13);
979
980
189k
    return(result & 0xFFFFFFFF);
981
189k
}
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
189k
xmlRandom(void) {
996
189k
#ifdef LIBXML_THREAD_ENABLED
997
189k
    return(xoroshiro64ss(xmlGetLocalRngState()));
998
#else
999
    return(xmlGlobalRandom());
1000
#endif
1001
189k
}
1002