Coverage Report

Created: 2022-11-15 06:15

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