Coverage Report

Created: 2023-03-26 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
2.65k
#define MAX_HASH_LEN 3
64
2.48M
#define MIN_DICT_SIZE 128
65
11
#define MAX_DICT_HASH 8 * 2048
66
#define WITH_BIG_KEY
67
68
#ifdef WITH_BIG_KEY
69
#define xmlDictComputeKey(dict, name, len)                              \
70
2.47M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
71
2.47M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
72
2.47M
     xmlDictComputeBigKey(name, len, (dict)->seed))
73
74
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
75
5.82k
    (((prefix) == NULL) ?                                               \
76
5.82k
      (xmlDictComputeKey(dict, name, len)) :                             \
77
5.82k
      (((dict)->size == MIN_DICT_SIZE) ?                                \
78
5.82k
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
79
5.82k
       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
2.42k
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
223
2.42k
    xmlDictStringsPtr pool;
224
2.42k
    const xmlChar *ret;
225
2.42k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
226
2.42k
    size_t limit = 0;
227
228
#ifdef DICT_DEBUG_PATTERNS
229
    fprintf(stderr, "-");
230
#endif
231
2.42k
    pool = dict->strings;
232
2.53k
    while (pool != NULL) {
233
2.28k
  if ((size_t)(pool->end - pool->free) > namelen)
234
2.17k
      goto found_pool;
235
111
  if (pool->size > size) size = pool->size;
236
111
        limit += pool->size;
237
111
  pool = pool->next;
238
111
    }
239
    /*
240
     * Not found, need to allocate
241
     */
242
251
    if (pool == NULL) {
243
251
        if ((dict->limit > 0) && (limit > dict->limit)) {
244
0
            return(NULL);
245
0
        }
246
247
251
        if (size == 0) size = 1000;
248
71
  else size *= 4; /* exponential growth */
249
251
        if (size < 4 * namelen)
250
48
      size = 4 * namelen; /* just in case ! */
251
251
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
252
251
  if (pool == NULL)
253
0
      return(NULL);
254
251
  pool->size = size;
255
251
  pool->nbStrings = 0;
256
251
  pool->free = &pool->array[0];
257
251
  pool->end = &pool->array[size];
258
251
  pool->next = dict->strings;
259
251
  dict->strings = pool;
260
#ifdef DICT_DEBUG_PATTERNS
261
        fprintf(stderr, "+");
262
#endif
263
251
    }
264
2.42k
found_pool:
265
2.42k
    ret = pool->free;
266
2.42k
    memcpy(pool->free, name, namelen);
267
2.42k
    pool->free += namelen;
268
2.42k
    *(pool->free++) = 0;
269
2.42k
    pool->nbStrings++;
270
2.42k
    return(ret);
271
251
}
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
206
{
289
206
    xmlDictStringsPtr pool;
290
206
    const xmlChar *ret;
291
206
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
292
206
    size_t limit = 0;
293
294
206
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
295
296
#ifdef DICT_DEBUG_PATTERNS
297
    fprintf(stderr, "=");
298
#endif
299
206
    pool = dict->strings;
300
247
    while (pool != NULL) {
301
235
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
302
194
      goto found_pool;
303
41
  if (pool->size > size) size = pool->size;
304
41
        limit += pool->size;
305
41
  pool = pool->next;
306
41
    }
307
    /*
308
     * Not found, need to allocate
309
     */
310
12
    if (pool == NULL) {
311
12
        if ((dict->limit > 0) && (limit > dict->limit)) {
312
0
            return(NULL);
313
0
        }
314
315
12
        if (size == 0) size = 1000;
316
12
  else size *= 4; /* exponential growth */
317
12
        if (size < 4 * (namelen + plen + 1))
318
0
      size = 4 * (namelen + plen + 1); /* just in case ! */
319
12
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
320
12
  if (pool == NULL)
321
0
      return(NULL);
322
12
  pool->size = size;
323
12
  pool->nbStrings = 0;
324
12
  pool->free = &pool->array[0];
325
12
  pool->end = &pool->array[size];
326
12
  pool->next = dict->strings;
327
12
  dict->strings = pool;
328
#ifdef DICT_DEBUG_PATTERNS
329
        fprintf(stderr, "+");
330
#endif
331
12
    }
332
206
found_pool:
333
206
    ret = pool->free;
334
206
    memcpy(pool->free, prefix, plen);
335
206
    pool->free += plen;
336
206
    *(pool->free++) = ':';
337
206
    memcpy(pool->free, name, namelen);
338
206
    pool->free += namelen;
339
206
    *(pool->free++) = 0;
340
206
    pool->nbStrings++;
341
206
    return(ret);
342
12
}
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
9.63k
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
361
9.63k
    uint32_t hash;
362
9.63k
    int i;
363
364
9.63k
    if (namelen <= 0 || data == NULL) return(0);
365
366
9.63k
    hash = seed;
367
368
2.68M
    for (i = 0;i < namelen; i++) {
369
2.67M
        hash += data[i];
370
2.67M
  hash += (hash << 10);
371
2.67M
  hash ^= (hash >> 6);
372
2.67M
    }
373
9.63k
    hash += (hash << 3);
374
9.63k
    hash ^= (hash >> 11);
375
9.63k
    hash += (hash << 15);
376
377
9.63k
    return hash;
378
9.63k
}
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
454
{
399
454
    uint32_t hash;
400
454
    int i;
401
402
454
    hash = seed;
403
404
136k
    for (i = 0;i < plen; i++) {
405
136k
        hash += prefix[i];
406
136k
  hash += (hash << 10);
407
136k
  hash ^= (hash >> 6);
408
136k
    }
409
454
    hash += ':';
410
454
    hash += (hash << 10);
411
454
    hash ^= (hash >> 6);
412
413
302k
    for (i = 0;i < len; i++) {
414
302k
        hash += name[i];
415
302k
  hash += (hash << 10);
416
302k
  hash ^= (hash >> 6);
417
302k
    }
418
454
    hash += (hash << 3);
419
454
    hash ^= (hash >> 11);
420
454
    hash += (hash << 15);
421
422
454
    return hash;
423
454
}
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
2.46M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
434
2.46M
    unsigned long value = seed;
435
436
2.46M
    if (name == NULL) return(0);
437
2.46M
    value += *name;
438
2.46M
    value <<= 5;
439
2.46M
    if (namelen > 10) {
440
12.8k
        value += name[namelen - 1];
441
12.8k
        namelen = 10;
442
12.8k
    }
443
2.46M
    switch (namelen) {
444
13.3k
        case 10: value += name[9];
445
        /* Falls through. */
446
17.9k
        case 9: value += name[8];
447
        /* Falls through. */
448
18.0k
        case 8: value += name[7];
449
        /* Falls through. */
450
23.5k
        case 7: value += name[6];
451
        /* Falls through. */
452
31.5k
        case 6: value += name[5];
453
        /* Falls through. */
454
2.42M
        case 5: value += name[4];
455
        /* Falls through. */
456
2.42M
        case 4: value += name[3];
457
        /* Falls through. */
458
2.43M
        case 3: value += name[2];
459
        /* Falls through. */
460
2.43M
        case 2: value += name[1];
461
        /* Falls through. */
462
2.46M
        default: break;
463
2.46M
    }
464
2.46M
    return(value);
465
2.46M
}
466
467
/*
468
 * xmlDictComputeFastQKey:
469
 *
470
 * Calculate a hash key for two strings using a fast hash function
471
 * that works well for low hash table fill.
472
 *
473
 * Neither of the two strings must be NULL.
474
 */
475
static unsigned long
476
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
477
                       const xmlChar *name, int len, int seed)
478
5.37k
{
479
5.37k
    unsigned long value = seed;
480
481
5.37k
    if (plen == 0)
482
0
  value += 30 * ':';
483
5.37k
    else
484
5.37k
  value += 30 * (*prefix);
485
486
5.37k
    if (len > 10) {
487
109
        int offset = len - (plen + 1 + 1);
488
109
  if (offset < 0)
489
11
      offset = len - (10 + 1);
490
109
  value += name[offset];
491
109
        len = 10;
492
109
  if (plen > 10)
493
28
      plen = 10;
494
109
    }
495
5.37k
    switch (plen) {
496
28
        case 10: value += prefix[9];
497
        /* Falls through. */
498
127
        case 9: value += prefix[8];
499
        /* Falls through. */
500
150
        case 8: value += prefix[7];
501
        /* Falls through. */
502
217
        case 7: value += prefix[6];
503
        /* Falls through. */
504
248
        case 6: value += prefix[5];
505
        /* Falls through. */
506
373
        case 5: value += prefix[4];
507
        /* Falls through. */
508
407
        case 4: value += prefix[3];
509
        /* Falls through. */
510
416
        case 3: value += prefix[2];
511
        /* Falls through. */
512
421
        case 2: value += prefix[1];
513
        /* Falls through. */
514
5.35k
        case 1: value += prefix[0];
515
        /* Falls through. */
516
5.37k
        default: break;
517
5.37k
    }
518
5.37k
    len -= plen;
519
5.37k
    if (len > 0) {
520
1.12k
        value += ':';
521
1.12k
  len--;
522
1.12k
    }
523
5.37k
    switch (len) {
524
0
        case 10: value += name[9];
525
        /* Falls through. */
526
0
        case 9: value += name[8];
527
        /* Falls through. */
528
65
        case 8: value += name[7];
529
        /* Falls through. */
530
66
        case 7: value += name[6];
531
        /* Falls through. */
532
66
        case 6: value += name[5];
533
        /* Falls through. */
534
85
        case 5: value += name[4];
535
        /* Falls through. */
536
87
        case 4: value += name[3];
537
        /* Falls through. */
538
87
        case 3: value += name[2];
539
        /* Falls through. */
540
118
        case 2: value += name[1];
541
        /* Falls through. */
542
1.08k
        case 1: value += name[0];
543
        /* Falls through. */
544
5.37k
        default: break;
545
5.37k
    }
546
5.37k
    return(value);
547
5.37k
}
548
549
/**
550
 * xmlDictCreate:
551
 *
552
 * Create a new dictionary
553
 *
554
 * Returns the newly created dictionary, or NULL if an error occurred.
555
 */
556
xmlDictPtr
557
311
xmlDictCreate(void) {
558
311
    xmlDictPtr dict;
559
560
311
    xmlInitParser();
561
562
#ifdef DICT_DEBUG_PATTERNS
563
    fprintf(stderr, "C");
564
#endif
565
566
311
    dict = xmlMalloc(sizeof(xmlDict));
567
311
    if (dict) {
568
311
        dict->ref_counter = 1;
569
311
        dict->limit = 0;
570
571
311
        dict->size = MIN_DICT_SIZE;
572
311
  dict->nbElems = 0;
573
311
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
574
311
  dict->strings = NULL;
575
311
  dict->subdict = NULL;
576
311
        if (dict->dict) {
577
311
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
578
#ifdef DICT_RANDOMIZATION
579
            dict->seed = __xmlRandom();
580
#else
581
311
            dict->seed = 0;
582
311
#endif
583
311
      return(dict);
584
311
        }
585
0
        xmlFree(dict);
586
0
    }
587
0
    return(NULL);
588
311
}
589
590
/**
591
 * xmlDictCreateSub:
592
 * @sub: an existing dictionary
593
 *
594
 * Create a new dictionary, inheriting strings from the read-only
595
 * dictionary @sub. On lookup, strings are first searched in the
596
 * new dictionary, then in @sub, and if not found are created in the
597
 * new dictionary.
598
 *
599
 * Returns the newly created dictionary, or NULL if an error occurred.
600
 */
601
xmlDictPtr
602
152
xmlDictCreateSub(xmlDictPtr sub) {
603
152
    xmlDictPtr dict = xmlDictCreate();
604
605
152
    if ((dict != NULL) && (sub != NULL)) {
606
#ifdef DICT_DEBUG_PATTERNS
607
        fprintf(stderr, "R");
608
#endif
609
152
        dict->seed = sub->seed;
610
152
        dict->subdict = sub;
611
152
  xmlDictReference(dict->subdict);
612
152
    }
613
152
    return(dict);
614
152
}
615
616
/**
617
 * xmlDictReference:
618
 * @dict: the dictionary
619
 *
620
 * Increment the reference counter of a dictionary
621
 *
622
 * Returns 0 in case of success and -1 in case of error
623
 */
624
int
625
8.25k
xmlDictReference(xmlDictPtr dict) {
626
8.25k
    if (dict == NULL) return -1;
627
8.25k
    xmlMutexLock(&xmlDictMutex);
628
8.25k
    dict->ref_counter++;
629
8.25k
    xmlMutexUnlock(&xmlDictMutex);
630
8.25k
    return(0);
631
8.25k
}
632
633
/**
634
 * xmlDictGrow:
635
 * @dict: the dictionary
636
 * @size: the new size of the dictionary
637
 *
638
 * resize the dictionary
639
 *
640
 * Returns 0 in case of success, -1 in case of failure
641
 */
642
static int
643
11
xmlDictGrow(xmlDictPtr dict, size_t size) {
644
11
    unsigned long key, okey;
645
11
    size_t oldsize, i;
646
11
    xmlDictEntryPtr iter, next;
647
11
    struct _xmlDictEntry *olddict;
648
#ifdef DEBUG_GROW
649
    unsigned long nbElem = 0;
650
#endif
651
11
    int ret = 0;
652
11
    int keep_keys = 1;
653
654
11
    if (dict == NULL)
655
0
  return(-1);
656
11
    if (size < 8)
657
0
        return(-1);
658
11
    if (size > 8 * 2048)
659
0
  return(-1);
660
661
#ifdef DICT_DEBUG_PATTERNS
662
    fprintf(stderr, "*");
663
#endif
664
665
11
    oldsize = dict->size;
666
11
    olddict = dict->dict;
667
11
    if (olddict == NULL)
668
0
        return(-1);
669
11
    if (oldsize == MIN_DICT_SIZE)
670
11
        keep_keys = 0;
671
672
11
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
673
11
    if (dict->dict == NULL) {
674
0
  dict->dict = olddict;
675
0
  return(-1);
676
0
    }
677
11
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
678
11
    dict->size = size;
679
680
    /*  If the two loops are merged, there would be situations where
681
  a new entry needs to allocated and data copied into it from
682
  the main dict. It is nicer to run through the array twice, first
683
  copying all the elements in the main array (less probability of
684
  allocate) and then the rest, so we only free in the second loop.
685
    */
686
1.41k
    for (i = 0; i < oldsize; i++) {
687
1.40k
  if (olddict[i].valid == 0)
688
981
      continue;
689
690
427
  if (keep_keys)
691
0
      okey = olddict[i].okey;
692
427
  else
693
427
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
694
427
  key = okey % dict->size;
695
696
427
  if (dict->dict[key].valid == 0) {
697
418
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
698
418
      dict->dict[key].next = NULL;
699
418
      dict->dict[key].okey = okey;
700
418
  } else {
701
9
      xmlDictEntryPtr entry;
702
703
9
      entry = xmlMalloc(sizeof(xmlDictEntry));
704
9
      if (entry != NULL) {
705
9
    entry->name = olddict[i].name;
706
9
    entry->len = olddict[i].len;
707
9
    entry->okey = okey;
708
9
    entry->next = dict->dict[key].next;
709
9
    entry->valid = 1;
710
9
    dict->dict[key].next = entry;
711
9
      } else {
712
          /*
713
     * we don't have much ways to alert from here
714
     * result is losing an entry and unicity guarantee
715
     */
716
0
          ret = -1;
717
0
      }
718
9
  }
719
#ifdef DEBUG_GROW
720
  nbElem++;
721
#endif
722
427
    }
723
724
1.41k
    for (i = 0; i < oldsize; i++) {
725
1.40k
  iter = olddict[i].next;
726
1.61k
  while (iter) {
727
206
      next = iter->next;
728
729
      /*
730
       * put back the entry in the new dict
731
       */
732
733
206
      if (keep_keys)
734
0
    okey = iter->okey;
735
206
      else
736
206
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
737
206
      key = okey % dict->size;
738
206
      if (dict->dict[key].valid == 0) {
739
200
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
740
200
    dict->dict[key].next = NULL;
741
200
    dict->dict[key].valid = 1;
742
200
    dict->dict[key].okey = okey;
743
200
    xmlFree(iter);
744
200
      } else {
745
6
    iter->next = dict->dict[key].next;
746
6
    iter->okey = okey;
747
6
    dict->dict[key].next = iter;
748
6
      }
749
750
#ifdef DEBUG_GROW
751
      nbElem++;
752
#endif
753
754
206
      iter = next;
755
206
  }
756
1.40k
    }
757
758
11
    xmlFree(olddict);
759
760
#ifdef DEBUG_GROW
761
    xmlGenericError(xmlGenericErrorContext,
762
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
763
#endif
764
765
11
    return(ret);
766
11
}
767
768
/**
769
 * xmlDictFree:
770
 * @dict: the dictionary
771
 *
772
 * Free the hash @dict and its contents. The userdata is
773
 * deallocated with @f if provided.
774
 */
775
void
776
8.56k
xmlDictFree(xmlDictPtr dict) {
777
8.56k
    size_t i;
778
8.56k
    xmlDictEntryPtr iter;
779
8.56k
    xmlDictEntryPtr next;
780
8.56k
    int inside_dict = 0;
781
8.56k
    xmlDictStringsPtr pool, nextp;
782
783
8.56k
    if (dict == NULL)
784
0
  return;
785
786
    /* decrement the counter, it may be shared by a parser and docs */
787
8.56k
    xmlMutexLock(&xmlDictMutex);
788
8.56k
    dict->ref_counter--;
789
8.56k
    if (dict->ref_counter > 0) {
790
8.25k
        xmlMutexUnlock(&xmlDictMutex);
791
8.25k
        return;
792
8.25k
    }
793
794
309
    xmlMutexUnlock(&xmlDictMutex);
795
796
309
    if (dict->subdict != NULL) {
797
152
        xmlDictFree(dict->subdict);
798
152
    }
799
800
309
    if (dict->dict) {
801
25.2k
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
802
24.9k
      iter = &(dict->dict[i]);
803
24.9k
      if (iter->valid == 0)
804
22.7k
    continue;
805
2.21k
      inside_dict = 1;
806
4.85k
      while (iter) {
807
2.63k
    next = iter->next;
808
2.63k
    if (!inside_dict)
809
414
        xmlFree(iter);
810
2.63k
    dict->nbElems--;
811
2.63k
    inside_dict = 0;
812
2.63k
    iter = next;
813
2.63k
      }
814
2.21k
  }
815
309
  xmlFree(dict->dict);
816
309
    }
817
309
    pool = dict->strings;
818
572
    while (pool != NULL) {
819
263
        nextp = pool->next;
820
263
  xmlFree(pool);
821
263
  pool = nextp;
822
263
    }
823
309
    xmlFree(dict);
824
309
}
825
826
/**
827
 * xmlDictLookup:
828
 * @dict: the dictionary
829
 * @name: the name of the userdata
830
 * @len: the length of the name, if -1 it is recomputed
831
 *
832
 * Add the @name to the dictionary @dict if not present.
833
 *
834
 * Returns the internal copy of the name or NULL in case of internal error
835
 */
836
const xmlChar *
837
2.47M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
838
2.47M
    unsigned long key, okey, nbi = 0;
839
2.47M
    xmlDictEntryPtr entry;
840
2.47M
    xmlDictEntryPtr insert;
841
2.47M
    const xmlChar *ret;
842
2.47M
    unsigned int l;
843
844
2.47M
    if ((dict == NULL) || (name == NULL))
845
0
  return(NULL);
846
847
2.47M
    if (len < 0)
848
2.39M
        l = strlen((const char *) name);
849
85.2k
    else
850
85.2k
        l = len;
851
852
2.47M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
853
2.47M
        (l > INT_MAX / 2))
854
0
        return(NULL);
855
856
    /*
857
     * Check for duplicate and insertion location.
858
     */
859
2.47M
    okey = xmlDictComputeKey(dict, name, l);
860
2.47M
    key = okey % dict->size;
861
2.47M
    if (dict->dict[key].valid == 0) {
862
1.87k
  insert = NULL;
863
2.47M
    } else {
864
2.49M
  for (insert = &(dict->dict[key]); insert->next != NULL;
865
2.47M
       insert = insert->next) {
866
46.3k
#ifdef __GNUC__
867
46.3k
      if ((insert->okey == okey) && (insert->len == l)) {
868
28.1k
    if (!memcmp(insert->name, name, l))
869
28.0k
        return(insert->name);
870
28.1k
      }
871
#else
872
      if ((insert->okey == okey) && (insert->len == l) &&
873
          (!xmlStrncmp(insert->name, name, l)))
874
    return(insert->name);
875
#endif
876
18.2k
      nbi++;
877
18.2k
  }
878
2.44M
#ifdef __GNUC__
879
2.44M
  if ((insert->okey == okey) && (insert->len == l)) {
880
2.44M
      if (!memcmp(insert->name, name, l))
881
2.44M
    return(insert->name);
882
2.44M
  }
883
#else
884
  if ((insert->okey == okey) && (insert->len == l) &&
885
      (!xmlStrncmp(insert->name, name, l)))
886
      return(insert->name);
887
#endif
888
2.44M
    }
889
890
2.42k
    if (dict->subdict) {
891
26
        unsigned long skey;
892
893
        /* we cannot always reuse the same okey for the subdict */
894
26
        if (((dict->size == MIN_DICT_SIZE) &&
895
26
       (dict->subdict->size != MIN_DICT_SIZE)) ||
896
26
            ((dict->size != MIN_DICT_SIZE) &&
897
26
       (dict->subdict->size == MIN_DICT_SIZE)))
898
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
899
26
  else
900
26
      skey = okey;
901
902
26
  key = skey % dict->subdict->size;
903
26
  if (dict->subdict->dict[key].valid != 0) {
904
0
      xmlDictEntryPtr tmp;
905
906
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
907
0
     tmp = tmp->next) {
908
0
#ifdef __GNUC__
909
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
910
0
        if (!memcmp(tmp->name, name, l))
911
0
      return(tmp->name);
912
0
    }
913
#else
914
    if ((tmp->okey == skey) && (tmp->len == l) &&
915
        (!xmlStrncmp(tmp->name, name, l)))
916
        return(tmp->name);
917
#endif
918
0
    nbi++;
919
0
      }
920
0
#ifdef __GNUC__
921
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
922
0
    if (!memcmp(tmp->name, name, l))
923
0
        return(tmp->name);
924
0
      }
925
#else
926
      if ((tmp->okey == skey) && (tmp->len == l) &&
927
    (!xmlStrncmp(tmp->name, name, l)))
928
    return(tmp->name);
929
#endif
930
0
  }
931
26
  key = okey % dict->size;
932
26
    }
933
934
2.42k
    ret = xmlDictAddString(dict, name, l);
935
2.42k
    if (ret == NULL)
936
0
        return(NULL);
937
2.42k
    if (insert == NULL) {
938
1.87k
  entry = &(dict->dict[key]);
939
1.87k
    } else {
940
554
  entry = xmlMalloc(sizeof(xmlDictEntry));
941
554
  if (entry == NULL)
942
0
       return(NULL);
943
554
    }
944
2.42k
    entry->name = ret;
945
2.42k
    entry->len = l;
946
2.42k
    entry->next = NULL;
947
2.42k
    entry->valid = 1;
948
2.42k
    entry->okey = okey;
949
950
951
2.42k
    if (insert != NULL)
952
554
  insert->next = entry;
953
954
2.42k
    dict->nbElems++;
955
956
2.42k
    if ((nbi > MAX_HASH_LEN) &&
957
2.42k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
958
10
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
959
0
      return(NULL);
960
10
    }
961
    /* Note that entry may have been freed at this point by xmlDictGrow */
962
963
2.42k
    return(ret);
964
2.42k
}
965
966
/**
967
 * xmlDictExists:
968
 * @dict: the dictionary
969
 * @name: the name of the userdata
970
 * @len: the length of the name, if -1 it is recomputed
971
 *
972
 * Check if the @name exists in the dictionary @dict.
973
 *
974
 * Returns the internal copy of the name or NULL if not found.
975
 */
976
const xmlChar *
977
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
978
0
    unsigned long key, okey;
979
0
    xmlDictEntryPtr insert;
980
0
    unsigned int l;
981
982
0
    if ((dict == NULL) || (name == NULL))
983
0
  return(NULL);
984
985
0
    if (len < 0)
986
0
        l = strlen((const char *) name);
987
0
    else
988
0
        l = len;
989
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
990
0
        (l > INT_MAX / 2))
991
0
        return(NULL);
992
993
    /*
994
     * Check for duplicate and insertion location.
995
     */
996
0
    okey = xmlDictComputeKey(dict, name, l);
997
0
    key = okey % dict->size;
998
0
    if (dict->dict[key].valid == 0) {
999
0
  insert = NULL;
1000
0
    } else {
1001
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1002
0
       insert = insert->next) {
1003
0
#ifdef __GNUC__
1004
0
      if ((insert->okey == okey) && (insert->len == l)) {
1005
0
    if (!memcmp(insert->name, name, l))
1006
0
        return(insert->name);
1007
0
      }
1008
#else
1009
      if ((insert->okey == okey) && (insert->len == l) &&
1010
          (!xmlStrncmp(insert->name, name, l)))
1011
    return(insert->name);
1012
#endif
1013
0
  }
1014
0
#ifdef __GNUC__
1015
0
  if ((insert->okey == okey) && (insert->len == l)) {
1016
0
      if (!memcmp(insert->name, name, l))
1017
0
    return(insert->name);
1018
0
  }
1019
#else
1020
  if ((insert->okey == okey) && (insert->len == l) &&
1021
      (!xmlStrncmp(insert->name, name, l)))
1022
      return(insert->name);
1023
#endif
1024
0
    }
1025
1026
0
    if (dict->subdict) {
1027
0
        unsigned long skey;
1028
1029
        /* we cannot always reuse the same okey for the subdict */
1030
0
        if (((dict->size == MIN_DICT_SIZE) &&
1031
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1032
0
            ((dict->size != MIN_DICT_SIZE) &&
1033
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1034
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1035
0
  else
1036
0
      skey = okey;
1037
1038
0
  key = skey % dict->subdict->size;
1039
0
  if (dict->subdict->dict[key].valid != 0) {
1040
0
      xmlDictEntryPtr tmp;
1041
1042
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1043
0
     tmp = tmp->next) {
1044
0
#ifdef __GNUC__
1045
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1046
0
        if (!memcmp(tmp->name, name, l))
1047
0
      return(tmp->name);
1048
0
    }
1049
#else
1050
    if ((tmp->okey == skey) && (tmp->len == l) &&
1051
        (!xmlStrncmp(tmp->name, name, l)))
1052
        return(tmp->name);
1053
#endif
1054
0
      }
1055
0
#ifdef __GNUC__
1056
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1057
0
    if (!memcmp(tmp->name, name, l))
1058
0
        return(tmp->name);
1059
0
      }
1060
#else
1061
      if ((tmp->okey == skey) && (tmp->len == l) &&
1062
    (!xmlStrncmp(tmp->name, name, l)))
1063
    return(tmp->name);
1064
#endif
1065
0
  }
1066
0
    }
1067
1068
    /* not found */
1069
0
    return(NULL);
1070
0
}
1071
1072
/**
1073
 * xmlDictQLookup:
1074
 * @dict: the dictionary
1075
 * @prefix: the prefix
1076
 * @name: the name
1077
 *
1078
 * Add the QName @prefix:@name to the hash @dict if not present.
1079
 *
1080
 * Returns the internal copy of the QName or NULL in case of internal error
1081
 */
1082
const xmlChar *
1083
5.82k
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1084
5.82k
    unsigned long okey, key, nbi = 0;
1085
5.82k
    xmlDictEntryPtr entry;
1086
5.82k
    xmlDictEntryPtr insert;
1087
5.82k
    const xmlChar *ret;
1088
5.82k
    unsigned int len, plen, l;
1089
1090
5.82k
    if ((dict == NULL) || (name == NULL))
1091
0
  return(NULL);
1092
5.82k
    if (prefix == NULL)
1093
0
        return(xmlDictLookup(dict, name, -1));
1094
1095
5.82k
    l = len = strlen((const char *) name);
1096
5.82k
    plen = strlen((const char *) prefix);
1097
5.82k
    len += 1 + plen;
1098
1099
    /*
1100
     * Check for duplicate and insertion location.
1101
     */
1102
5.82k
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1103
5.82k
    key = okey % dict->size;
1104
5.82k
    if (dict->dict[key].valid == 0) {
1105
155
  insert = NULL;
1106
5.67k
    } else {
1107
5.92k
  for (insert = &(dict->dict[key]); insert->next != NULL;
1108
5.67k
       insert = insert->next) {
1109
1.19k
      if ((insert->okey == okey) && (insert->len == len) &&
1110
1.19k
          (xmlStrQEqual(prefix, name, insert->name)))
1111
942
    return(insert->name);
1112
252
      nbi++;
1113
252
  }
1114
4.73k
  if ((insert->okey == okey) && (insert->len == len) &&
1115
4.73k
      (xmlStrQEqual(prefix, name, insert->name)))
1116
4.68k
      return(insert->name);
1117
4.73k
    }
1118
1119
206
    if (dict->subdict) {
1120
0
        unsigned long skey;
1121
1122
        /* we cannot always reuse the same okey for the subdict */
1123
0
        if (((dict->size == MIN_DICT_SIZE) &&
1124
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1125
0
            ((dict->size != MIN_DICT_SIZE) &&
1126
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1127
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1128
0
  else
1129
0
      skey = okey;
1130
1131
0
  key = skey % dict->subdict->size;
1132
0
  if (dict->subdict->dict[key].valid != 0) {
1133
0
      xmlDictEntryPtr tmp;
1134
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1135
0
     tmp = tmp->next) {
1136
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1137
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1138
0
        return(tmp->name);
1139
0
    nbi++;
1140
0
      }
1141
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1142
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1143
0
    return(tmp->name);
1144
0
  }
1145
0
  key = okey % dict->size;
1146
0
    }
1147
1148
206
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1149
206
    if (ret == NULL)
1150
0
        return(NULL);
1151
206
    if (insert == NULL) {
1152
155
  entry = &(dict->dict[key]);
1153
155
    } else {
1154
51
  entry = xmlMalloc(sizeof(xmlDictEntry));
1155
51
  if (entry == NULL)
1156
0
       return(NULL);
1157
51
    }
1158
206
    entry->name = ret;
1159
206
    entry->len = len;
1160
206
    entry->next = NULL;
1161
206
    entry->valid = 1;
1162
206
    entry->okey = okey;
1163
1164
206
    if (insert != NULL)
1165
51
  insert->next = entry;
1166
1167
206
    dict->nbElems++;
1168
1169
206
    if ((nbi > MAX_HASH_LEN) &&
1170
206
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1171
1
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1172
    /* Note that entry may have been freed at this point by xmlDictGrow */
1173
1174
206
    return(ret);
1175
206
}
1176
1177
/**
1178
 * xmlDictOwns:
1179
 * @dict: the dictionary
1180
 * @str: the string
1181
 *
1182
 * check if a string is owned by the dictionary
1183
 *
1184
 * Returns 1 if true, 0 if false and -1 in case of error
1185
 * -1 in case of error
1186
 */
1187
int
1188
7.26M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1189
7.26M
    xmlDictStringsPtr pool;
1190
1191
7.26M
    if ((dict == NULL) || (str == NULL))
1192
0
  return(-1);
1193
7.26M
    pool = dict->strings;
1194
9.75M
    while (pool != NULL) {
1195
4.91M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1196
2.42M
      return(1);
1197
2.49M
  pool = pool->next;
1198
2.49M
    }
1199
4.84M
    if (dict->subdict)
1200
2.40M
        return(xmlDictOwns(dict->subdict, str));
1201
2.43M
    return(0);
1202
4.84M
}
1203
1204
/**
1205
 * xmlDictSize:
1206
 * @dict: the dictionary
1207
 *
1208
 * Query the number of elements installed in the hash @dict.
1209
 *
1210
 * Returns the number of elements in the dictionary or
1211
 * -1 in case of error
1212
 */
1213
int
1214
0
xmlDictSize(xmlDictPtr dict) {
1215
0
    if (dict == NULL)
1216
0
  return(-1);
1217
0
    if (dict->subdict)
1218
0
        return(dict->nbElems + dict->subdict->nbElems);
1219
0
    return(dict->nbElems);
1220
0
}
1221
1222
/**
1223
 * xmlDictSetLimit:
1224
 * @dict: the dictionary
1225
 * @limit: the limit in bytes
1226
 *
1227
 * Set a size limit for the dictionary
1228
 * Added in 2.9.0
1229
 *
1230
 * Returns the previous limit of the dictionary or 0
1231
 */
1232
size_t
1233
157
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1234
157
    size_t ret;
1235
1236
157
    if (dict == NULL)
1237
0
  return(0);
1238
157
    ret = dict->limit;
1239
157
    dict->limit = limit;
1240
157
    return(ret);
1241
157
}
1242
1243
/**
1244
 * xmlDictGetUsage:
1245
 * @dict: the dictionary
1246
 *
1247
 * Get how much memory is used by a dictionary for strings
1248
 * Added in 2.9.0
1249
 *
1250
 * Returns the amount of strings allocated
1251
 */
1252
size_t
1253
0
xmlDictGetUsage(xmlDictPtr dict) {
1254
0
    xmlDictStringsPtr pool;
1255
0
    size_t limit = 0;
1256
1257
0
    if (dict == NULL)
1258
0
  return(0);
1259
0
    pool = dict->strings;
1260
0
    while (pool != NULL) {
1261
0
        limit += pool->size;
1262
0
  pool = pool->next;
1263
0
    }
1264
0
    return(limit);
1265
0
}
1266