Coverage Report

Created: 2025-07-23 08:13

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