Coverage Report

Created: 2023-06-07 06:14

/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 <limits.h>
23
#include <stdlib.h>
24
#include <time.h>
25
26
#include "private/dict.h"
27
#include "private/threads.h"
28
29
/*
30
 * Following http://www.ocert.org/advisories/ocert-2011-003.html
31
 * it seems that having hash randomization might be a good idea
32
 * when using XML with untrusted data
33
 * Note1: that it works correctly only if compiled with WITH_BIG_KEY
34
 *  which is the default.
35
 * Note2: the fast function used for a small dict won't protect very
36
 *  well but since the attack is based on growing a very big hash
37
 *  list we will use the BigKey algo as soon as the hash size grows
38
 *  over MIN_DICT_SIZE so this actually works
39
 */
40
#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
41
#define DICT_RANDOMIZATION
42
#endif
43
44
#include <string.h>
45
#ifdef HAVE_STDINT_H
46
#include <stdint.h>
47
#else
48
#ifdef HAVE_INTTYPES_H
49
#include <inttypes.h>
50
#elif defined(_WIN32)
51
typedef unsigned __int32 uint32_t;
52
#endif
53
#endif
54
#include <libxml/tree.h>
55
#include <libxml/dict.h>
56
#include <libxml/xmlmemory.h>
57
#include <libxml/xmlerror.h>
58
#include <libxml/globals.h>
59
60
/* #define DEBUG_GROW */
61
/* #define DICT_DEBUG_PATTERNS */
62
63
1.16M
#define MAX_HASH_LEN 3
64
23.2M
#define MIN_DICT_SIZE 128
65
5.47k
#define MAX_DICT_HASH 100000000
66
#define WITH_BIG_KEY
67
68
#ifdef WITH_BIG_KEY
69
#define xmlDictComputeKey(dict, name, len)                              \
70
17.1M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
71
17.1M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
72
17.1M
     xmlDictComputeBigKey(name, len, (dict)->seed))
73
74
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
75
167k
    (((prefix) == NULL) ?                                               \
76
167k
      (xmlDictComputeKey(dict, name, len)) :                             \
77
167k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
78
167k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
79
167k
       xmlDictComputeBigQKey(prefix, plen, name, len, (dict)->seed)))
80
81
#else /* !WITH_BIG_KEY */
82
#define xmlDictComputeKey(dict, name, len)                              \
83
        xmlDictComputeFastKey(name, len, (dict)->seed)
84
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
85
        xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed)
86
#endif /* WITH_BIG_KEY */
87
88
/*
89
 * An entry in the dictionary
90
 */
91
typedef struct _xmlDictEntry xmlDictEntry;
92
typedef xmlDictEntry *xmlDictEntryPtr;
93
struct _xmlDictEntry {
94
    struct _xmlDictEntry *next;
95
    const xmlChar *name;
96
    unsigned int len;
97
    int valid;
98
    unsigned long okey;
99
};
100
101
typedef struct _xmlDictStrings xmlDictStrings;
102
typedef xmlDictStrings *xmlDictStringsPtr;
103
struct _xmlDictStrings {
104
    xmlDictStringsPtr next;
105
    xmlChar *free;
106
    xmlChar *end;
107
    size_t size;
108
    size_t nbStrings;
109
    xmlChar array[1];
110
};
111
/*
112
 * The entire dictionary
113
 */
114
struct _xmlDict {
115
    int ref_counter;
116
117
    struct _xmlDictEntry *dict;
118
    size_t size;
119
    unsigned int nbElems;
120
    xmlDictStringsPtr strings;
121
122
    struct _xmlDict *subdict;
123
    /* used for randomization */
124
    int seed;
125
    /* used to impose a limit on size */
126
    size_t limit;
127
};
128
129
/*
130
 * A mutex for modifying the reference counter for shared
131
 * dictionaries.
132
 */
133
static xmlMutex xmlDictMutex;
134
135
#ifdef DICT_RANDOMIZATION
136
#ifdef HAVE_RAND_R
137
/*
138
 * Internal data for random function, protected by xmlDictMutex
139
 */
140
static unsigned int rand_seed = 0;
141
#endif
142
#endif
143
144
/**
145
 * xmlInitializeDict:
146
 *
147
 * DEPRECATED: Alias for xmlInitParser.
148
 */
149
0
int xmlInitializeDict(void) {
150
0
    xmlInitParser();
151
0
    return(0);
152
0
}
153
154
/**
155
 * __xmlInitializeDict:
156
 *
157
 * This function is not public
158
 * Do the dictionary mutex initialization.
159
 */
160
2
int __xmlInitializeDict(void) {
161
2
    xmlInitMutex(&xmlDictMutex);
162
163
#ifdef DICT_RANDOMIZATION
164
#ifdef HAVE_RAND_R
165
    rand_seed = time(NULL);
166
    rand_r(& rand_seed);
167
#else
168
    srand(time(NULL));
169
#endif
170
#endif
171
2
    return(1);
172
2
}
173
174
#ifdef DICT_RANDOMIZATION
175
int __xmlRandom(void) {
176
    int ret;
177
178
    xmlMutexLock(&xmlDictMutex);
179
#ifdef HAVE_RAND_R
180
    ret = rand_r(& rand_seed);
181
#else
182
    ret = rand();
183
#endif
184
    xmlMutexUnlock(&xmlDictMutex);
185
    return(ret);
186
}
187
#endif
188
189
/**
190
 * xmlDictCleanup:
191
 *
192
 * DEPRECATED: This function is a no-op. Call xmlCleanupParser
193
 * to free global state but see the warnings there. xmlCleanupParser
194
 * should be only called once at program exit. In most cases, you don't
195
 * have call cleanup functions at all.
196
 */
197
void
198
0
xmlDictCleanup(void) {
199
0
}
200
201
/**
202
 * xmlCleanupDictInternal:
203
 *
204
 * Free the dictionary mutex.
205
 */
206
void
207
0
xmlCleanupDictInternal(void) {
208
0
    xmlCleanupMutex(&xmlDictMutex);
209
0
}
210
211
/*
212
 * xmlDictAddString:
213
 * @dict: the dictionary
214
 * @name: the name of the userdata
215
 * @len: the length of the name
216
 *
217
 * Add the string to the array[s]
218
 *
219
 * Returns the pointer of the local string, or NULL in case of error.
220
 */
221
static const xmlChar *
222
1.13M
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
223
1.13M
    xmlDictStringsPtr pool;
224
1.13M
    const xmlChar *ret;
225
1.13M
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
226
1.13M
    size_t limit = 0;
227
228
#ifdef DICT_DEBUG_PATTERNS
229
    fprintf(stderr, "-");
230
#endif
231
1.13M
    pool = dict->strings;
232
1.14M
    while (pool != NULL) {
233
1.06M
  if ((size_t)(pool->end - pool->free) > namelen)
234
1.05M
      goto found_pool;
235
8.36k
  if (pool->size > size) size = pool->size;
236
8.36k
        limit += pool->size;
237
8.36k
  pool = pool->next;
238
8.36k
    }
239
    /*
240
     * Not found, need to allocate
241
     */
242
77.0k
    if (pool == NULL) {
243
77.0k
        if ((dict->limit > 0) && (limit > dict->limit)) {
244
69
            return(NULL);
245
69
        }
246
247
76.9k
        if (size == 0) size = 1000;
248
6.03k
  else size *= 4; /* exponential growth */
249
76.9k
        if (size < 4 * namelen)
250
1.40k
      size = 4 * namelen; /* just in case ! */
251
76.9k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
252
76.9k
  if (pool == NULL)
253
2.58k
      return(NULL);
254
74.3k
  pool->size = size;
255
74.3k
  pool->nbStrings = 0;
256
74.3k
  pool->free = &pool->array[0];
257
74.3k
  pool->end = &pool->array[size];
258
74.3k
  pool->next = dict->strings;
259
74.3k
  dict->strings = pool;
260
#ifdef DICT_DEBUG_PATTERNS
261
        fprintf(stderr, "+");
262
#endif
263
74.3k
    }
264
1.13M
found_pool:
265
1.13M
    ret = pool->free;
266
1.13M
    memcpy(pool->free, name, namelen);
267
1.13M
    pool->free += namelen;
268
1.13M
    *(pool->free++) = 0;
269
1.13M
    pool->nbStrings++;
270
1.13M
    return(ret);
271
77.0k
}
272
273
/*
274
 * xmlDictAddQString:
275
 * @dict: the dictionary
276
 * @prefix: the prefix of the userdata
277
 * @plen: the prefix length
278
 * @name: the name of the userdata
279
 * @len: the length of the name
280
 *
281
 * Add the QName to the array[s]
282
 *
283
 * Returns the pointer of the local string, or NULL in case of error.
284
 */
285
static const xmlChar *
286
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, unsigned int plen,
287
                 const xmlChar *name, unsigned int namelen)
288
29.5k
{
289
29.5k
    xmlDictStringsPtr pool;
290
29.5k
    const xmlChar *ret;
291
29.5k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
292
29.5k
    size_t limit = 0;
293
294
29.5k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
295
296
#ifdef DICT_DEBUG_PATTERNS
297
    fprintf(stderr, "=");
298
#endif
299
29.5k
    pool = dict->strings;
300
29.6k
    while (pool != NULL) {
301
29.2k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
302
29.1k
      goto found_pool;
303
169
  if (pool->size > size) size = pool->size;
304
169
        limit += pool->size;
305
169
  pool = pool->next;
306
169
    }
307
    /*
308
     * Not found, need to allocate
309
     */
310
384
    if (pool == NULL) {
311
384
        if ((dict->limit > 0) && (limit > dict->limit)) {
312
0
            return(NULL);
313
0
        }
314
315
384
        if (size == 0) size = 1000;
316
94
  else size *= 4; /* exponential growth */
317
384
        if (size < 4 * (namelen + plen + 1))
318
2
      size = 4 * (namelen + plen + 1); /* just in case ! */
319
384
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
320
384
  if (pool == NULL)
321
97
      return(NULL);
322
287
  pool->size = size;
323
287
  pool->nbStrings = 0;
324
287
  pool->free = &pool->array[0];
325
287
  pool->end = &pool->array[size];
326
287
  pool->next = dict->strings;
327
287
  dict->strings = pool;
328
#ifdef DICT_DEBUG_PATTERNS
329
        fprintf(stderr, "+");
330
#endif
331
287
    }
332
29.4k
found_pool:
333
29.4k
    ret = pool->free;
334
29.4k
    memcpy(pool->free, prefix, plen);
335
29.4k
    pool->free += plen;
336
29.4k
    *(pool->free++) = ':';
337
29.4k
    memcpy(pool->free, name, namelen);
338
29.4k
    pool->free += namelen;
339
29.4k
    *(pool->free++) = 0;
340
29.4k
    pool->nbStrings++;
341
29.4k
    return(ret);
342
384
}
343
344
#ifdef WITH_BIG_KEY
345
/*
346
 * xmlDictComputeBigKey:
347
 *
348
 * Calculate a hash key using a good hash function that works well for
349
 * larger hash table sizes.
350
 *
351
 * Hash function by "One-at-a-Time Hash" see
352
 * http://burtleburtle.net/bob/hash/doobs.html
353
 */
354
355
#ifdef __clang__
356
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
357
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
358
#endif
359
static uint32_t
360
2.93M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
361
2.93M
    uint32_t hash;
362
2.93M
    int i;
363
364
2.93M
    if (namelen <= 0 || data == NULL) return(0);
365
366
2.90M
    hash = seed;
367
368
788M
    for (i = 0;i < namelen; i++) {
369
785M
        hash += data[i];
370
785M
  hash += (hash << 10);
371
785M
  hash ^= (hash >> 6);
372
785M
    }
373
2.90M
    hash += (hash << 3);
374
2.90M
    hash ^= (hash >> 11);
375
2.90M
    hash += (hash << 15);
376
377
2.90M
    return hash;
378
2.93M
}
379
380
/*
381
 * xmlDictComputeBigQKey:
382
 *
383
 * Calculate a hash key for two strings using a good hash function
384
 * that works well for larger hash table sizes.
385
 *
386
 * Hash function by "One-at-a-Time Hash" see
387
 * http://burtleburtle.net/bob/hash/doobs.html
388
 *
389
 * Neither of the two strings must be NULL.
390
 */
391
#ifdef __clang__
392
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
393
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
394
#endif
395
static unsigned long
396
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
397
                      const xmlChar *name, int len, int seed)
398
23.1k
{
399
23.1k
    uint32_t hash;
400
23.1k
    int i;
401
402
23.1k
    hash = seed;
403
404
149k
    for (i = 0;i < plen; i++) {
405
126k
        hash += prefix[i];
406
126k
  hash += (hash << 10);
407
126k
  hash ^= (hash >> 6);
408
126k
    }
409
23.1k
    hash += ':';
410
23.1k
    hash += (hash << 10);
411
23.1k
    hash ^= (hash >> 6);
412
413
193k
    for (i = 0;i < len; i++) {
414
170k
        hash += name[i];
415
170k
  hash += (hash << 10);
416
170k
  hash ^= (hash >> 6);
417
170k
    }
418
23.1k
    hash += (hash << 3);
419
23.1k
    hash ^= (hash >> 11);
420
23.1k
    hash += (hash << 15);
421
422
23.1k
    return hash;
423
23.1k
}
424
#endif /* WITH_BIG_KEY */
425
426
/*
427
 * xmlDictComputeFastKey:
428
 *
429
 * Calculate a hash key using a fast hash function that works well
430
 * for low hash table fill.
431
 */
432
static unsigned long
433
14.2M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
434
14.2M
    unsigned long value = seed;
435
436
14.2M
    if ((name == NULL) || (namelen <= 0))
437
74.4k
        return(value);
438
14.1M
    value += *name;
439
14.1M
    value <<= 5;
440
14.1M
    if (namelen > 10) {
441
2.31M
        value += name[namelen - 1];
442
2.31M
        namelen = 10;
443
2.31M
    }
444
14.1M
    switch (namelen) {
445
2.87M
        case 10: value += name[9];
446
        /* Falls through. */
447
3.31M
        case 9: value += name[8];
448
        /* Falls through. */
449
4.11M
        case 8: value += name[7];
450
        /* Falls through. */
451
5.18M
        case 7: value += name[6];
452
        /* Falls through. */
453
6.27M
        case 6: value += name[5];
454
        /* Falls through. */
455
7.59M
        case 5: value += name[4];
456
        /* Falls through. */
457
9.64M
        case 4: value += name[3];
458
        /* Falls through. */
459
11.1M
        case 3: value += name[2];
460
        /* Falls through. */
461
11.7M
        case 2: value += name[1];
462
        /* Falls through. */
463
14.1M
        default: break;
464
14.1M
    }
465
14.1M
    return(value);
466
14.1M
}
467
468
/*
469
 * xmlDictComputeFastQKey:
470
 *
471
 * Calculate a hash key for two strings using a fast hash function
472
 * that works well for low hash table fill.
473
 *
474
 * Neither of the two strings must be NULL.
475
 */
476
static unsigned long
477
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
478
                       const xmlChar *name, int len, int seed)
479
144k
{
480
144k
    unsigned long value = seed;
481
482
144k
    if (plen == 0)
483
0
  value += 30 * ':';
484
144k
    else
485
144k
  value += 30 * (*prefix);
486
487
144k
    if (len > 10) {
488
17.6k
        int offset = len - (plen + 1 + 1);
489
17.6k
  if (offset < 0)
490
1.17k
      offset = len - (10 + 1);
491
17.6k
  value += name[offset];
492
17.6k
        len = 10;
493
17.6k
  if (plen > 10)
494
1.59k
      plen = 10;
495
17.6k
    }
496
144k
    switch (plen) {
497
2.12k
        case 10: value += prefix[9];
498
        /* Falls through. */
499
3.97k
        case 9: value += prefix[8];
500
        /* Falls through. */
501
8.45k
        case 8: value += prefix[7];
502
        /* Falls through. */
503
12.7k
        case 7: value += prefix[6];
504
        /* Falls through. */
505
19.3k
        case 6: value += prefix[5];
506
        /* Falls through. */
507
21.9k
        case 5: value += prefix[4];
508
        /* Falls through. */
509
27.6k
        case 4: value += prefix[3];
510
        /* Falls through. */
511
49.1k
        case 3: value += prefix[2];
512
        /* Falls through. */
513
94.6k
        case 2: value += prefix[1];
514
        /* Falls through. */
515
140k
        case 1: value += prefix[0];
516
        /* Falls through. */
517
144k
        default: break;
518
144k
    }
519
144k
    len -= plen;
520
144k
    if (len > 0) {
521
110k
        value += ':';
522
110k
  len--;
523
110k
    }
524
144k
    switch (len) {
525
0
        case 10: value += name[9];
526
        /* Falls through. */
527
0
        case 9: value += name[8];
528
        /* Falls through. */
529
10.4k
        case 8: value += name[7];
530
        /* Falls through. */
531
15.2k
        case 7: value += name[6];
532
        /* Falls through. */
533
24.9k
        case 6: value += name[5];
534
        /* Falls through. */
535
34.9k
        case 5: value += name[4];
536
        /* Falls through. */
537
37.9k
        case 4: value += name[3];
538
        /* Falls through. */
539
41.1k
        case 3: value += name[2];
540
        /* Falls through. */
541
53.4k
        case 2: value += name[1];
542
        /* Falls through. */
543
101k
        case 1: value += name[0];
544
        /* Falls through. */
545
144k
        default: break;
546
144k
    }
547
144k
    return(value);
548
144k
}
549
550
/**
551
 * xmlDictCreate:
552
 *
553
 * Create a new dictionary
554
 *
555
 * Returns the newly created dictionary, or NULL if an error occurred.
556
 */
557
xmlDictPtr
558
497k
xmlDictCreate(void) {
559
497k
    xmlDictPtr dict;
560
561
497k
    xmlInitParser();
562
563
#ifdef DICT_DEBUG_PATTERNS
564
    fprintf(stderr, "C");
565
#endif
566
567
497k
    dict = xmlMalloc(sizeof(xmlDict));
568
497k
    if (dict) {
569
497k
        dict->ref_counter = 1;
570
497k
        dict->limit = 0;
571
572
497k
        dict->size = MIN_DICT_SIZE;
573
497k
  dict->nbElems = 0;
574
497k
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
575
497k
  dict->strings = NULL;
576
497k
  dict->subdict = NULL;
577
497k
        if (dict->dict) {
578
497k
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
579
#ifdef DICT_RANDOMIZATION
580
            dict->seed = __xmlRandom();
581
#else
582
497k
            dict->seed = 0;
583
497k
#endif
584
497k
      return(dict);
585
497k
        }
586
32
        xmlFree(dict);
587
32
    }
588
115
    return(NULL);
589
497k
}
590
591
/**
592
 * xmlDictCreateSub:
593
 * @sub: an existing dictionary
594
 *
595
 * Create a new dictionary, inheriting strings from the read-only
596
 * dictionary @sub. On lookup, strings are first searched in the
597
 * new dictionary, then in @sub, and if not found are created in the
598
 * new dictionary.
599
 *
600
 * Returns the newly created dictionary, or NULL if an error occurred.
601
 */
602
xmlDictPtr
603
14.1k
xmlDictCreateSub(xmlDictPtr sub) {
604
14.1k
    xmlDictPtr dict = xmlDictCreate();
605
606
14.1k
    if ((dict != NULL) && (sub != NULL)) {
607
#ifdef DICT_DEBUG_PATTERNS
608
        fprintf(stderr, "R");
609
#endif
610
14.1k
        dict->seed = sub->seed;
611
14.1k
        dict->subdict = sub;
612
14.1k
  xmlDictReference(dict->subdict);
613
14.1k
    }
614
14.1k
    return(dict);
615
14.1k
}
616
617
/**
618
 * xmlDictReference:
619
 * @dict: the dictionary
620
 *
621
 * Increment the reference counter of a dictionary
622
 *
623
 * Returns 0 in case of success and -1 in case of error
624
 */
625
int
626
1.50M
xmlDictReference(xmlDictPtr dict) {
627
1.50M
    if (dict == NULL) return -1;
628
1.49M
    xmlMutexLock(&xmlDictMutex);
629
1.49M
    dict->ref_counter++;
630
1.49M
    xmlMutexUnlock(&xmlDictMutex);
631
1.49M
    return(0);
632
1.50M
}
633
634
/**
635
 * xmlDictGrow:
636
 * @dict: the dictionary
637
 * @size: the new size of the dictionary
638
 *
639
 * resize the dictionary
640
 *
641
 * Returns 0 in case of success, -1 in case of failure
642
 */
643
static int
644
2.66k
xmlDictGrow(xmlDictPtr dict, size_t size) {
645
2.66k
    unsigned long key, okey;
646
2.66k
    size_t oldsize, i;
647
2.66k
    xmlDictEntryPtr iter, next;
648
2.66k
    struct _xmlDictEntry *olddict;
649
#ifdef DEBUG_GROW
650
    unsigned long nbElem = 0;
651
#endif
652
2.66k
    int ret = 0;
653
2.66k
    int keep_keys = 1;
654
655
2.66k
    if (dict == NULL)
656
0
  return(-1);
657
2.66k
    if (size < 8)
658
0
        return(-1);
659
2.66k
    if (size > MAX_DICT_HASH)
660
0
  return(-1);
661
662
#ifdef DICT_DEBUG_PATTERNS
663
    fprintf(stderr, "*");
664
#endif
665
666
2.66k
    oldsize = dict->size;
667
2.66k
    olddict = dict->dict;
668
2.66k
    if (olddict == NULL)
669
0
        return(-1);
670
2.66k
    if (oldsize == MIN_DICT_SIZE)
671
2.57k
        keep_keys = 0;
672
673
2.66k
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
674
2.66k
    if (dict->dict == NULL) {
675
3
  dict->dict = olddict;
676
3
  return(-1);
677
3
    }
678
2.66k
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
679
2.66k
    dict->size = size;
680
681
    /*  If the two loops are merged, there would be situations where
682
  a new entry needs to allocated and data copied into it from
683
  the main dict. It is nicer to run through the array twice, first
684
  copying all the elements in the main array (less probability of
685
  allocate) and then the rest, so we only free in the second loop.
686
    */
687
57.9M
    for (i = 0; i < oldsize; i++) {
688
57.8M
  if (olddict[i].valid == 0)
689
57.8M
      continue;
690
691
78.1k
  if (keep_keys)
692
2.91k
      okey = olddict[i].okey;
693
75.2k
  else
694
75.2k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
695
78.1k
  key = okey % dict->size;
696
697
78.1k
  if (dict->dict[key].valid == 0) {
698
76.5k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
699
76.5k
      dict->dict[key].next = NULL;
700
76.5k
      dict->dict[key].okey = okey;
701
76.5k
  } else {
702
1.61k
      xmlDictEntryPtr entry;
703
704
1.61k
      entry = xmlMalloc(sizeof(xmlDictEntry));
705
1.61k
      if (entry != NULL) {
706
1.60k
    entry->name = olddict[i].name;
707
1.60k
    entry->len = olddict[i].len;
708
1.60k
    entry->okey = okey;
709
1.60k
    entry->next = dict->dict[key].next;
710
1.60k
    entry->valid = 1;
711
1.60k
    dict->dict[key].next = entry;
712
1.60k
      } else {
713
          /*
714
     * we don't have much ways to alert from here
715
     * result is losing an entry and unicity guarantee
716
     */
717
11
          ret = -1;
718
11
      }
719
1.61k
  }
720
#ifdef DEBUG_GROW
721
  nbElem++;
722
#endif
723
78.1k
    }
724
725
57.9M
    for (i = 0; i < oldsize; i++) {
726
57.8M
  iter = olddict[i].next;
727
57.9M
  while (iter) {
728
37.0k
      next = iter->next;
729
730
      /*
731
       * put back the entry in the new dict
732
       */
733
734
37.0k
      if (keep_keys)
735
588
    okey = iter->okey;
736
36.4k
      else
737
36.4k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
738
37.0k
      key = okey % dict->size;
739
37.0k
      if (dict->dict[key].valid == 0) {
740
34.7k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
741
34.7k
    dict->dict[key].next = NULL;
742
34.7k
    dict->dict[key].valid = 1;
743
34.7k
    dict->dict[key].okey = okey;
744
34.7k
    xmlFree(iter);
745
34.7k
      } else {
746
2.28k
    iter->next = dict->dict[key].next;
747
2.28k
    iter->okey = okey;
748
2.28k
    dict->dict[key].next = iter;
749
2.28k
      }
750
751
#ifdef DEBUG_GROW
752
      nbElem++;
753
#endif
754
755
37.0k
      iter = next;
756
37.0k
  }
757
57.8M
    }
758
759
2.66k
    xmlFree(olddict);
760
761
#ifdef DEBUG_GROW
762
    xmlGenericError(xmlGenericErrorContext,
763
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
764
#endif
765
766
2.66k
    return(ret);
767
2.66k
}
768
769
/**
770
 * xmlDictFree:
771
 * @dict: the dictionary
772
 *
773
 * Free the hash @dict and its contents. The userdata is
774
 * deallocated with @f if provided.
775
 */
776
void
777
1.99M
xmlDictFree(xmlDictPtr dict) {
778
1.99M
    size_t i;
779
1.99M
    xmlDictEntryPtr iter;
780
1.99M
    xmlDictEntryPtr next;
781
1.99M
    int inside_dict = 0;
782
1.99M
    xmlDictStringsPtr pool, nextp;
783
784
1.99M
    if (dict == NULL)
785
54
  return;
786
787
    /* decrement the counter, it may be shared by a parser and docs */
788
1.99M
    xmlMutexLock(&xmlDictMutex);
789
1.99M
    dict->ref_counter--;
790
1.99M
    if (dict->ref_counter > 0) {
791
1.49M
        xmlMutexUnlock(&xmlDictMutex);
792
1.49M
        return;
793
1.49M
    }
794
795
497k
    xmlMutexUnlock(&xmlDictMutex);
796
797
497k
    if (dict->subdict != NULL) {
798
14.1k
        xmlDictFree(dict->subdict);
799
14.1k
    }
800
801
497k
    if (dict->dict) {
802
273M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
803
272M
      iter = &(dict->dict[i]);
804
272M
      if (iter->valid == 0)
805
271M
    continue;
806
934k
      inside_dict = 1;
807
2.09M
      while (iter) {
808
1.16M
    next = iter->next;
809
1.16M
    if (!inside_dict)
810
226k
        xmlFree(iter);
811
1.16M
    dict->nbElems--;
812
1.16M
    inside_dict = 0;
813
1.16M
    iter = next;
814
1.16M
      }
815
934k
  }
816
497k
  xmlFree(dict->dict);
817
497k
    }
818
497k
    pool = dict->strings;
819
572k
    while (pool != NULL) {
820
74.6k
        nextp = pool->next;
821
74.6k
  xmlFree(pool);
822
74.6k
  pool = nextp;
823
74.6k
    }
824
497k
    xmlFree(dict);
825
497k
}
826
827
/**
828
 * xmlDictLookup:
829
 * @dict: the dictionary
830
 * @name: the name of the userdata
831
 * @len: the length of the name, if -1 it is recomputed
832
 *
833
 * Add the @name to the dictionary @dict if not present.
834
 *
835
 * Returns the internal copy of the name or NULL in case of internal error
836
 */
837
const xmlChar *
838
16.7M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
839
16.7M
    unsigned long key, okey, nbi = 0;
840
16.7M
    xmlDictEntryPtr entry;
841
16.7M
    xmlDictEntryPtr insert;
842
16.7M
    const xmlChar *ret;
843
16.7M
    unsigned int l;
844
845
16.7M
    if ((dict == NULL) || (name == NULL))
846
40.7k
  return(NULL);
847
848
16.7M
    if (len < 0)
849
6.12M
        l = strlen((const char *) name);
850
10.5M
    else
851
10.5M
        l = len;
852
853
16.7M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
854
16.7M
        (l > INT_MAX / 2))
855
0
        return(NULL);
856
857
    /*
858
     * Check for duplicate and insertion location.
859
     */
860
16.7M
    okey = xmlDictComputeKey(dict, name, l);
861
16.7M
    key = okey % dict->size;
862
16.7M
    if (dict->dict[key].valid == 0) {
863
2.22M
  insert = NULL;
864
14.4M
    } else {
865
18.2M
  for (insert = &(dict->dict[key]); insert->next != NULL;
866
14.4M
       insert = insert->next) {
867
7.05M
#ifdef __GNUC__
868
7.05M
      if ((insert->okey == okey) && (insert->len == l)) {
869
3.41M
    if (!memcmp(insert->name, name, l))
870
3.28M
        return(insert->name);
871
3.41M
      }
872
#else
873
      if ((insert->okey == okey) && (insert->len == l) &&
874
          (!xmlStrncmp(insert->name, name, l)))
875
    return(insert->name);
876
#endif
877
3.76M
      nbi++;
878
3.76M
  }
879
11.1M
#ifdef __GNUC__
880
11.1M
  if ((insert->okey == okey) && (insert->len == l)) {
881
10.7M
      if (!memcmp(insert->name, name, l))
882
10.7M
    return(insert->name);
883
10.7M
  }
884
#else
885
  if ((insert->okey == okey) && (insert->len == l) &&
886
      (!xmlStrncmp(insert->name, name, l)))
887
      return(insert->name);
888
#endif
889
11.1M
    }
890
891
2.70M
    if (dict->subdict) {
892
1.57M
        unsigned long skey;
893
894
        /* we cannot always reuse the same okey for the subdict */
895
1.57M
        if (((dict->size == MIN_DICT_SIZE) &&
896
1.57M
       (dict->subdict->size != MIN_DICT_SIZE)) ||
897
1.57M
            ((dict->size != MIN_DICT_SIZE) &&
898
1.31M
       (dict->subdict->size == MIN_DICT_SIZE)))
899
323k
      skey = xmlDictComputeKey(dict->subdict, name, l);
900
1.25M
  else
901
1.25M
      skey = okey;
902
903
1.57M
  key = skey % dict->subdict->size;
904
1.57M
  if (dict->subdict->dict[key].valid != 0) {
905
1.57M
      xmlDictEntryPtr tmp;
906
907
2.18M
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
908
1.57M
     tmp = tmp->next) {
909
1.15M
#ifdef __GNUC__
910
1.15M
    if ((tmp->okey == skey) && (tmp->len == l)) {
911
551k
        if (!memcmp(tmp->name, name, l))
912
541k
      return(tmp->name);
913
551k
    }
914
#else
915
    if ((tmp->okey == skey) && (tmp->len == l) &&
916
        (!xmlStrncmp(tmp->name, name, l)))
917
        return(tmp->name);
918
#endif
919
610k
    nbi++;
920
610k
      }
921
1.02M
#ifdef __GNUC__
922
1.02M
      if ((tmp->okey == skey) && (tmp->len == l)) {
923
1.02M
    if (!memcmp(tmp->name, name, l))
924
1.02M
        return(tmp->name);
925
1.02M
      }
926
#else
927
      if ((tmp->okey == skey) && (tmp->len == l) &&
928
    (!xmlStrncmp(tmp->name, name, l)))
929
    return(tmp->name);
930
#endif
931
1.02M
  }
932
10.8k
  key = okey % dict->size;
933
10.8k
    }
934
935
1.13M
    ret = xmlDictAddString(dict, name, l);
936
1.13M
    if (ret == NULL)
937
2.65k
        return(NULL);
938
1.13M
    if (insert == NULL) {
939
876k
  entry = &(dict->dict[key]);
940
876k
    } else {
941
255k
  entry = xmlMalloc(sizeof(xmlDictEntry));
942
255k
  if (entry == NULL)
943
570
       return(NULL);
944
255k
    }
945
1.13M
    entry->name = ret;
946
1.13M
    entry->len = l;
947
1.13M
    entry->next = NULL;
948
1.13M
    entry->valid = 1;
949
1.13M
    entry->okey = okey;
950
951
952
1.13M
    if (insert != NULL)
953
254k
  insert->next = entry;
954
955
1.13M
    dict->nbElems++;
956
957
1.13M
    if ((nbi > MAX_HASH_LEN) &&
958
1.13M
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
959
2.64k
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
960
7
      return(NULL);
961
2.64k
    }
962
    /* Note that entry may have been freed at this point by xmlDictGrow */
963
964
1.13M
    return(ret);
965
1.13M
}
966
967
/**
968
 * xmlDictExists:
969
 * @dict: the dictionary
970
 * @name: the name of the userdata
971
 * @len: the length of the name, if -1 it is recomputed
972
 *
973
 * Check if the @name exists in the dictionary @dict.
974
 *
975
 * Returns the internal copy of the name or NULL if not found.
976
 */
977
const xmlChar *
978
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
979
0
    unsigned long key, okey;
980
0
    xmlDictEntryPtr insert;
981
0
    unsigned int l;
982
983
0
    if ((dict == NULL) || (name == NULL))
984
0
  return(NULL);
985
986
0
    if (len < 0)
987
0
        l = strlen((const char *) name);
988
0
    else
989
0
        l = len;
990
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
991
0
        (l > INT_MAX / 2))
992
0
        return(NULL);
993
994
    /*
995
     * Check for duplicate and insertion location.
996
     */
997
0
    okey = xmlDictComputeKey(dict, name, l);
998
0
    key = okey % dict->size;
999
0
    if (dict->dict[key].valid == 0) {
1000
0
  insert = NULL;
1001
0
    } else {
1002
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1003
0
       insert = insert->next) {
1004
0
#ifdef __GNUC__
1005
0
      if ((insert->okey == okey) && (insert->len == l)) {
1006
0
    if (!memcmp(insert->name, name, l))
1007
0
        return(insert->name);
1008
0
      }
1009
#else
1010
      if ((insert->okey == okey) && (insert->len == l) &&
1011
          (!xmlStrncmp(insert->name, name, l)))
1012
    return(insert->name);
1013
#endif
1014
0
  }
1015
0
#ifdef __GNUC__
1016
0
  if ((insert->okey == okey) && (insert->len == l)) {
1017
0
      if (!memcmp(insert->name, name, l))
1018
0
    return(insert->name);
1019
0
  }
1020
#else
1021
  if ((insert->okey == okey) && (insert->len == l) &&
1022
      (!xmlStrncmp(insert->name, name, l)))
1023
      return(insert->name);
1024
#endif
1025
0
    }
1026
1027
0
    if (dict->subdict) {
1028
0
        unsigned long skey;
1029
1030
        /* we cannot always reuse the same okey for the subdict */
1031
0
        if (((dict->size == MIN_DICT_SIZE) &&
1032
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1033
0
            ((dict->size != MIN_DICT_SIZE) &&
1034
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1035
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1036
0
  else
1037
0
      skey = okey;
1038
1039
0
  key = skey % dict->subdict->size;
1040
0
  if (dict->subdict->dict[key].valid != 0) {
1041
0
      xmlDictEntryPtr tmp;
1042
1043
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1044
0
     tmp = tmp->next) {
1045
0
#ifdef __GNUC__
1046
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1047
0
        if (!memcmp(tmp->name, name, l))
1048
0
      return(tmp->name);
1049
0
    }
1050
#else
1051
    if ((tmp->okey == skey) && (tmp->len == l) &&
1052
        (!xmlStrncmp(tmp->name, name, l)))
1053
        return(tmp->name);
1054
#endif
1055
0
      }
1056
0
#ifdef __GNUC__
1057
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1058
0
    if (!memcmp(tmp->name, name, l))
1059
0
        return(tmp->name);
1060
0
      }
1061
#else
1062
      if ((tmp->okey == skey) && (tmp->len == l) &&
1063
    (!xmlStrncmp(tmp->name, name, l)))
1064
    return(tmp->name);
1065
#endif
1066
0
  }
1067
0
    }
1068
1069
    /* not found */
1070
0
    return(NULL);
1071
0
}
1072
1073
/**
1074
 * xmlDictQLookup:
1075
 * @dict: the dictionary
1076
 * @prefix: the prefix
1077
 * @name: the name
1078
 *
1079
 * Add the QName @prefix:@name to the hash @dict if not present.
1080
 *
1081
 * Returns the internal copy of the QName or NULL in case of internal error
1082
 */
1083
const xmlChar *
1084
167k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1085
167k
    unsigned long okey, key, nbi = 0;
1086
167k
    xmlDictEntryPtr entry;
1087
167k
    xmlDictEntryPtr insert;
1088
167k
    const xmlChar *ret;
1089
167k
    unsigned int len, plen, l;
1090
1091
167k
    if ((dict == NULL) || (name == NULL))
1092
0
  return(NULL);
1093
167k
    if (prefix == NULL)
1094
0
        return(xmlDictLookup(dict, name, -1));
1095
1096
167k
    l = len = strlen((const char *) name);
1097
167k
    plen = strlen((const char *) prefix);
1098
167k
    len += 1 + plen;
1099
1100
    /*
1101
     * Check for duplicate and insertion location.
1102
     */
1103
167k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1104
167k
    key = okey % dict->size;
1105
167k
    if (dict->dict[key].valid == 0) {
1106
24.6k
  insert = NULL;
1107
142k
    } else {
1108
174k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1109
142k
       insert = insert->next) {
1110
48.5k
      if ((insert->okey == okey) && (insert->len == len) &&
1111
48.5k
          (xmlStrQEqual(prefix, name, insert->name)))
1112
16.3k
    return(insert->name);
1113
32.1k
      nbi++;
1114
32.1k
  }
1115
126k
  if ((insert->okey == okey) && (insert->len == len) &&
1116
126k
      (xmlStrQEqual(prefix, name, insert->name)))
1117
121k
      return(insert->name);
1118
126k
    }
1119
1120
29.5k
    if (dict->subdict) {
1121
432
        unsigned long skey;
1122
1123
        /* we cannot always reuse the same okey for the subdict */
1124
432
        if (((dict->size == MIN_DICT_SIZE) &&
1125
432
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1126
432
            ((dict->size != MIN_DICT_SIZE) &&
1127
386
       (dict->subdict->size == MIN_DICT_SIZE)))
1128
52
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1129
380
  else
1130
380
      skey = okey;
1131
1132
432
  key = skey % dict->subdict->size;
1133
432
  if (dict->subdict->dict[key].valid != 0) {
1134
107
      xmlDictEntryPtr tmp;
1135
130
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1136
107
     tmp = tmp->next) {
1137
27
    if ((tmp->okey == skey) && (tmp->len == len) &&
1138
27
        (xmlStrQEqual(prefix, name, tmp->name)))
1139
4
        return(tmp->name);
1140
23
    nbi++;
1141
23
      }
1142
103
      if ((tmp->okey == skey) && (tmp->len == len) &&
1143
103
    (xmlStrQEqual(prefix, name, tmp->name)))
1144
10
    return(tmp->name);
1145
103
  }
1146
418
  key = okey % dict->size;
1147
418
    }
1148
1149
29.5k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1150
29.5k
    if (ret == NULL)
1151
97
        return(NULL);
1152
29.4k
    if (insert == NULL) {
1153
24.5k
  entry = &(dict->dict[key]);
1154
24.5k
    } else {
1155
4.86k
  entry = xmlMalloc(sizeof(xmlDictEntry));
1156
4.86k
  if (entry == NULL)
1157
22
       return(NULL);
1158
4.86k
    }
1159
29.3k
    entry->name = ret;
1160
29.3k
    entry->len = len;
1161
29.3k
    entry->next = NULL;
1162
29.3k
    entry->valid = 1;
1163
29.3k
    entry->okey = okey;
1164
1165
29.3k
    if (insert != NULL)
1166
4.84k
  insert->next = entry;
1167
1168
29.3k
    dict->nbElems++;
1169
1170
29.3k
    if ((nbi > MAX_HASH_LEN) &&
1171
29.3k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1172
27
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1173
    /* Note that entry may have been freed at this point by xmlDictGrow */
1174
1175
29.3k
    return(ret);
1176
29.4k
}
1177
1178
/**
1179
 * xmlDictOwns:
1180
 * @dict: the dictionary
1181
 * @str: the string
1182
 *
1183
 * check if a string is owned by the dictionary
1184
 *
1185
 * Returns 1 if true, 0 if false and -1 in case of error
1186
 * -1 in case of error
1187
 */
1188
int
1189
27.3M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1190
27.3M
    xmlDictStringsPtr pool;
1191
1192
27.3M
    if ((dict == NULL) || (str == NULL))
1193
41
  return(-1);
1194
27.3M
    pool = dict->strings;
1195
49.1M
    while (pool != NULL) {
1196
34.5M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1197
12.6M
      return(1);
1198
21.8M
  pool = pool->next;
1199
21.8M
    }
1200
14.6M
    if (dict->subdict)
1201
5.57M
        return(xmlDictOwns(dict->subdict, str));
1202
9.07M
    return(0);
1203
14.6M
}
1204
1205
/**
1206
 * xmlDictSize:
1207
 * @dict: the dictionary
1208
 *
1209
 * Query the number of elements installed in the hash @dict.
1210
 *
1211
 * Returns the number of elements in the dictionary or
1212
 * -1 in case of error
1213
 */
1214
int
1215
0
xmlDictSize(xmlDictPtr dict) {
1216
0
    if (dict == NULL)
1217
0
  return(-1);
1218
0
    if (dict->subdict)
1219
0
        return(dict->nbElems + dict->subdict->nbElems);
1220
0
    return(dict->nbElems);
1221
0
}
1222
1223
/**
1224
 * xmlDictSetLimit:
1225
 * @dict: the dictionary
1226
 * @limit: the limit in bytes
1227
 *
1228
 * Set a size limit for the dictionary
1229
 * Added in 2.9.0
1230
 *
1231
 * Returns the previous limit of the dictionary or 0
1232
 */
1233
size_t
1234
420k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1235
420k
    size_t ret;
1236
1237
420k
    if (dict == NULL)
1238
0
  return(0);
1239
420k
    ret = dict->limit;
1240
420k
    dict->limit = limit;
1241
420k
    return(ret);
1242
420k
}
1243
1244
/**
1245
 * xmlDictGetUsage:
1246
 * @dict: the dictionary
1247
 *
1248
 * Get how much memory is used by a dictionary for strings
1249
 * Added in 2.9.0
1250
 *
1251
 * Returns the amount of strings allocated
1252
 */
1253
size_t
1254
0
xmlDictGetUsage(xmlDictPtr dict) {
1255
0
    xmlDictStringsPtr pool;
1256
0
    size_t limit = 0;
1257
1258
0
    if (dict == NULL)
1259
0
  return(0);
1260
0
    pool = dict->strings;
1261
0
    while (pool != NULL) {
1262
0
        limit += pool->size;
1263
0
  pool = pool->next;
1264
0
    }
1265
0
    return(limit);
1266
0
}
1267