Coverage Report

Created: 2026-03-12 06:42

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