Coverage Report

Created: 2026-04-29 07:28

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.74M
#define MAX_HASH_LEN 3
65
64.4M
#define MIN_DICT_SIZE 128
66
7.73k
#define MAX_DICT_HASH 8 * 2048
67
#define WITH_BIG_KEY
68
69
#ifdef WITH_BIG_KEY
70
#define xmlDictComputeKey(dict, name, len)                              \
71
61.9M
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
72
61.9M
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
73
61.9M
     xmlDictComputeBigKey(name, len, (dict)->seed))
74
75
#define xmlDictComputeQKey(dict, prefix, plen, name, len)               \
76
1.18M
    (((prefix) == NULL) ?                                               \
77
1.18M
      (xmlDictComputeKey(dict, name, len)) :                             \
78
1.18M
      (((dict)->size == MIN_DICT_SIZE) ?                                \
79
1.18M
       xmlDictComputeFastQKey(prefix, plen, name, len, (dict)->seed) :  \
80
1.18M
       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
546k
int __xmlRandom(void) {
198
546k
    int ret;
199
200
546k
    if (xmlDictInitialized == 0)
201
0
        __xmlInitializeDict();
202
203
546k
    xmlRMutexLock(xmlDictMutex);
204
546k
#ifdef HAVE_RAND_R
205
546k
    ret = rand_r(& rand_seed);
206
#else
207
    ret = rand();
208
#endif
209
546k
    xmlRMutexUnlock(xmlDictMutex);
210
546k
    return(ret);
211
546k
}
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.56M
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
242
3.56M
    xmlDictStringsPtr pool;
243
3.56M
    const xmlChar *ret;
244
3.56M
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
245
3.56M
    size_t limit = 0;
246
247
#ifdef DICT_DEBUG_PATTERNS
248
    fprintf(stderr, "-");
249
#endif
250
3.56M
    pool = dict->strings;
251
3.58M
    while (pool != NULL) {
252
3.33M
  if ((size_t)(pool->end - pool->free) > namelen)
253
3.31M
      goto found_pool;
254
20.3k
  if (pool->size > size) size = pool->size;
255
20.3k
        limit += pool->size;
256
20.3k
  pool = pool->next;
257
20.3k
    }
258
    /*
259
     * Not found, need to allocate
260
     */
261
247k
    if (pool == NULL) {
262
247k
        if ((dict->limit > 0) && (limit > dict->limit)) {
263
0
            return(NULL);
264
0
        }
265
266
247k
        if (size == 0) size = 1000;
267
12.4k
  else size *= 4; /* exponential growth */
268
247k
        if (size < 4 * namelen)
269
5.34k
      size = 4 * namelen; /* just in case ! */
270
247k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
271
247k
  if (pool == NULL)
272
0
      return(NULL);
273
247k
  pool->size = size;
274
247k
  pool->nbStrings = 0;
275
247k
  pool->free = &pool->array[0];
276
247k
  pool->end = &pool->array[size];
277
247k
  pool->next = dict->strings;
278
247k
  dict->strings = pool;
279
#ifdef DICT_DEBUG_PATTERNS
280
        fprintf(stderr, "+");
281
#endif
282
247k
    }
283
3.56M
found_pool:
284
3.56M
    ret = pool->free;
285
3.56M
    memcpy(pool->free, name, namelen);
286
3.56M
    pool->free += namelen;
287
3.56M
    *(pool->free++) = 0;
288
3.56M
    pool->nbStrings++;
289
3.56M
    return(ret);
290
247k
}
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
169k
{
308
169k
    xmlDictStringsPtr pool;
309
169k
    const xmlChar *ret;
310
169k
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
311
169k
    size_t limit = 0;
312
313
169k
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
314
315
#ifdef DICT_DEBUG_PATTERNS
316
    fprintf(stderr, "=");
317
#endif
318
169k
    pool = dict->strings;
319
172k
    while (pool != NULL) {
320
171k
  if ((size_t)(pool->end - pool->free) > namelen + plen + 1)
321
168k
      goto found_pool;
322
3.13k
  if (pool->size > size) size = pool->size;
323
3.13k
        limit += pool->size;
324
3.13k
  pool = pool->next;
325
3.13k
    }
326
    /*
327
     * Not found, need to allocate
328
     */
329
1.08k
    if (pool == NULL) {
330
1.08k
        if ((dict->limit > 0) && (limit > dict->limit)) {
331
0
            return(NULL);
332
0
        }
333
334
1.08k
        if (size == 0) size = 1000;
335
1.08k
  else size *= 4; /* exponential growth */
336
1.08k
        if (size < 4 * (namelen + plen + 1))
337
2
      size = 4 * (namelen + plen + 1); /* just in case ! */
338
1.08k
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
339
1.08k
  if (pool == NULL)
340
0
      return(NULL);
341
1.08k
  pool->size = size;
342
1.08k
  pool->nbStrings = 0;
343
1.08k
  pool->free = &pool->array[0];
344
1.08k
  pool->end = &pool->array[size];
345
1.08k
  pool->next = dict->strings;
346
1.08k
  dict->strings = pool;
347
#ifdef DICT_DEBUG_PATTERNS
348
        fprintf(stderr, "+");
349
#endif
350
1.08k
    }
351
169k
found_pool:
352
169k
    ret = pool->free;
353
169k
    memcpy(pool->free, prefix, plen);
354
169k
    pool->free += plen;
355
169k
    *(pool->free++) = ':';
356
169k
    memcpy(pool->free, name, namelen);
357
169k
    pool->free += namelen;
358
169k
    *(pool->free++) = 0;
359
169k
    pool->nbStrings++;
360
169k
    return(ret);
361
1.08k
}
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
12.0M
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
376
12.0M
    uint32_t hash;
377
12.0M
    int i;
378
379
12.0M
    if (namelen <= 0 || data == NULL) return(0);
380
381
12.0M
    hash = seed;
382
383
496M
    for (i = 0;i < namelen; i++) {
384
484M
        hash += data[i];
385
484M
  hash += (hash << 10);
386
484M
  hash ^= (hash >> 6);
387
484M
    }
388
12.0M
    hash += (hash << 3);
389
12.0M
    hash ^= (hash >> 11);
390
12.0M
    hash += (hash << 15);
391
392
12.0M
    return hash;
393
12.0M
}
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
758k
{
410
758k
    uint32_t hash;
411
758k
    int i;
412
413
758k
    hash = seed;
414
415
4.49M
    for (i = 0;i < plen; i++) {
416
3.73M
        hash += prefix[i];
417
3.73M
  hash += (hash << 10);
418
3.73M
  hash ^= (hash >> 6);
419
3.73M
    }
420
758k
    hash += ':';
421
758k
    hash += (hash << 10);
422
758k
    hash ^= (hash >> 6);
423
424
27.1M
    for (i = 0;i < len; i++) {
425
26.4M
        hash += name[i];
426
26.4M
  hash += (hash << 10);
427
26.4M
  hash ^= (hash >> 6);
428
26.4M
    }
429
758k
    hash += (hash << 3);
430
758k
    hash ^= (hash >> 11);
431
758k
    hash += (hash << 15);
432
433
758k
    return hash;
434
758k
}
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
49.8M
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
445
49.8M
    unsigned long value = seed;
446
447
49.8M
    if (name == NULL) return(0);
448
49.8M
    value = *name;
449
49.8M
    value <<= 5;
450
49.8M
    if (namelen > 10) {
451
9.83M
        value += name[namelen - 1];
452
9.83M
        namelen = 10;
453
9.83M
    }
454
49.8M
    switch (namelen) {
455
10.7M
        case 10: value += name[9];
456
        /* Falls through. */
457
11.8M
        case 9: value += name[8];
458
        /* Falls through. */
459
12.4M
        case 8: value += name[7];
460
        /* Falls through. */
461
13.6M
        case 7: value += name[6];
462
        /* Falls through. */
463
15.7M
        case 6: value += name[5];
464
        /* Falls through. */
465
26.0M
        case 5: value += name[4];
466
        /* Falls through. */
467
29.9M
        case 4: value += name[3];
468
        /* Falls through. */
469
34.5M
        case 3: value += name[2];
470
        /* Falls through. */
471
39.4M
        case 2: value += name[1];
472
        /* Falls through. */
473
49.8M
        default: break;
474
49.8M
    }
475
49.8M
    return(value);
476
49.8M
}
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
429k
{
490
429k
    unsigned long value = (unsigned long) seed;
491
492
429k
    if (plen == 0)
493
0
  value += 30 * (unsigned long) ':';
494
429k
    else
495
429k
  value += 30 * (*prefix);
496
497
429k
    if (len > 10) {
498
95.0k
        int offset = len - (plen + 1 + 1);
499
95.0k
  if (offset < 0)
500
10.1k
      offset = len - (10 + 1);
501
95.0k
  value += name[offset];
502
95.0k
        len = 10;
503
95.0k
  if (plen > 10)
504
15.5k
      plen = 10;
505
95.0k
    }
506
429k
    switch (plen) {
507
24.4k
        case 10: value += prefix[9];
508
        /* Falls through. */
509
30.3k
        case 9: value += prefix[8];
510
        /* Falls through. */
511
49.0k
        case 8: value += prefix[7];
512
        /* Falls through. */
513
73.3k
        case 7: value += prefix[6];
514
        /* Falls through. */
515
95.4k
        case 6: value += prefix[5];
516
        /* Falls through. */
517
124k
        case 5: value += prefix[4];
518
        /* Falls through. */
519
143k
        case 4: value += prefix[3];
520
        /* Falls through. */
521
195k
        case 3: value += prefix[2];
522
        /* Falls through. */
523
315k
        case 2: value += prefix[1];
524
        /* Falls through. */
525
402k
        case 1: value += prefix[0];
526
        /* Falls through. */
527
429k
        default: break;
528
429k
    }
529
429k
    len -= plen;
530
429k
    if (len > 0) {
531
246k
        value += (unsigned long) ':';
532
246k
  len--;
533
246k
    }
534
429k
    switch (len) {
535
0
        case 10: value += name[9];
536
        /* Falls through. */
537
0
        case 9: value += name[8];
538
        /* Falls through. */
539
9.76k
        case 8: value += name[7];
540
        /* Falls through. */
541
49.7k
        case 7: value += name[6];
542
        /* Falls through. */
543
85.2k
        case 6: value += name[5];
544
        /* Falls through. */
545
120k
        case 5: value += name[4];
546
        /* Falls through. */
547
144k
        case 4: value += name[3];
548
        /* Falls through. */
549
166k
        case 3: value += name[2];
550
        /* Falls through. */
551
189k
        case 2: value += name[1];
552
        /* Falls through. */
553
219k
        case 1: value += name[0];
554
        /* Falls through. */
555
429k
        default: break;
556
429k
    }
557
429k
    return(value);
558
429k
}
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
435k
xmlDictCreate(void) {
569
435k
    xmlDictPtr dict;
570
571
435k
    if (!xmlDictInitialized)
572
0
        if (!__xmlInitializeDict())
573
0
            return(NULL);
574
575
#ifdef DICT_DEBUG_PATTERNS
576
    fprintf(stderr, "C");
577
#endif
578
579
435k
    dict = xmlMalloc(sizeof(xmlDict));
580
435k
    if (dict) {
581
435k
        dict->ref_counter = 1;
582
435k
        dict->limit = 0;
583
584
435k
        dict->size = MIN_DICT_SIZE;
585
435k
  dict->nbElems = 0;
586
435k
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
587
435k
  dict->strings = NULL;
588
435k
  dict->subdict = NULL;
589
435k
        if (dict->dict) {
590
435k
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
591
435k
#ifdef DICT_RANDOMIZATION
592
435k
            dict->seed = __xmlRandom();
593
#else
594
            dict->seed = 0;
595
#endif
596
435k
      return(dict);
597
435k
        }
598
0
        xmlFree(dict);
599
0
    }
600
0
    return(NULL);
601
435k
}
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
341k
xmlDictReference(xmlDictPtr dict) {
639
341k
    if (!xmlDictInitialized)
640
0
        if (!__xmlInitializeDict())
641
0
            return(-1);
642
643
341k
    if (dict == NULL) return -1;
644
341k
    xmlRMutexLock(xmlDictMutex);
645
341k
    dict->ref_counter++;
646
341k
    xmlRMutexUnlock(xmlDictMutex);
647
341k
    return(0);
648
341k
}
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.87k
xmlDictGrow(xmlDictPtr dict, size_t size) {
661
5.87k
    unsigned long key, okey;
662
5.87k
    size_t oldsize, i;
663
5.87k
    xmlDictEntryPtr iter, next;
664
5.87k
    struct _xmlDictEntry *olddict;
665
#ifdef DEBUG_GROW
666
    unsigned long nbElem = 0;
667
#endif
668
5.87k
    int ret = 0;
669
5.87k
    int keep_keys = 1;
670
671
5.87k
    if (dict == NULL)
672
0
  return(-1);
673
5.87k
    if (size < 8)
674
0
        return(-1);
675
5.87k
    if (size > 8 * 2048)
676
0
  return(-1);
677
678
#ifdef DICT_DEBUG_PATTERNS
679
    fprintf(stderr, "*");
680
#endif
681
682
5.87k
    oldsize = dict->size;
683
5.87k
    olddict = dict->dict;
684
5.87k
    if (olddict == NULL)
685
0
        return(-1);
686
5.87k
    if (oldsize == MIN_DICT_SIZE)
687
5.82k
        keep_keys = 0;
688
689
5.87k
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
690
5.87k
    if (dict->dict == NULL) {
691
0
  dict->dict = olddict;
692
0
  return(-1);
693
0
    }
694
5.87k
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
695
5.87k
    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
787k
    for (i = 0; i < oldsize; i++) {
704
781k
  if (olddict[i].valid == 0)
705
537k
      continue;
706
707
244k
  if (keep_keys)
708
21.8k
      okey = olddict[i].okey;
709
222k
  else
710
222k
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
711
244k
  key = okey % dict->size;
712
713
244k
  if (dict->dict[key].valid == 0) {
714
236k
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
715
236k
      dict->dict[key].next = NULL;
716
236k
      dict->dict[key].okey = okey;
717
236k
  } else {
718
8.21k
      xmlDictEntryPtr entry;
719
720
8.21k
      entry = xmlMalloc(sizeof(xmlDictEntry));
721
8.21k
      if (entry != NULL) {
722
8.21k
    entry->name = olddict[i].name;
723
8.21k
    entry->len = olddict[i].len;
724
8.21k
    entry->okey = okey;
725
8.21k
    entry->next = dict->dict[key].next;
726
8.21k
    entry->valid = 1;
727
8.21k
    dict->dict[key].next = entry;
728
8.21k
      } 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
8.21k
  }
736
#ifdef DEBUG_GROW
737
  nbElem++;
738
#endif
739
244k
    }
740
741
787k
    for (i = 0; i < oldsize; i++) {
742
781k
  iter = olddict[i].next;
743
936k
  while (iter) {
744
155k
      next = iter->next;
745
746
      /*
747
       * put back the entry in the new dict
748
       */
749
750
155k
      if (keep_keys)
751
14.0k
    okey = iter->okey;
752
141k
      else
753
141k
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
754
155k
      key = okey % dict->size;
755
155k
      if (dict->dict[key].valid == 0) {
756
136k
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
757
136k
    dict->dict[key].next = NULL;
758
136k
    dict->dict[key].valid = 1;
759
136k
    dict->dict[key].okey = okey;
760
136k
    xmlFree(iter);
761
136k
      } else {
762
18.7k
    iter->next = dict->dict[key].next;
763
18.7k
    iter->okey = okey;
764
18.7k
    dict->dict[key].next = iter;
765
18.7k
      }
766
767
#ifdef DEBUG_GROW
768
      nbElem++;
769
#endif
770
771
155k
      iter = next;
772
155k
  }
773
781k
    }
774
775
5.87k
    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.87k
    return(ret);
783
5.87k
}
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
777k
xmlDictFree(xmlDictPtr dict) {
794
777k
    size_t i;
795
777k
    xmlDictEntryPtr iter;
796
777k
    xmlDictEntryPtr next;
797
777k
    int inside_dict = 0;
798
777k
    xmlDictStringsPtr pool, nextp;
799
800
777k
    if (dict == NULL)
801
0
  return;
802
803
777k
    if (!xmlDictInitialized)
804
0
        if (!__xmlInitializeDict())
805
0
            return;
806
807
    /* decrement the counter, it may be shared by a parser and docs */
808
777k
    xmlRMutexLock(xmlDictMutex);
809
777k
    dict->ref_counter--;
810
777k
    if (dict->ref_counter > 0) {
811
341k
        xmlRMutexUnlock(xmlDictMutex);
812
341k
        return;
813
341k
    }
814
815
435k
    xmlRMutexUnlock(xmlDictMutex);
816
817
435k
    if (dict->subdict != NULL) {
818
0
        xmlDictFree(dict->subdict);
819
0
    }
820
821
435k
    if (dict->dict) {
822
29.0M
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
823
28.6M
      iter = &(dict->dict[i]);
824
28.6M
      if (iter->valid == 0)
825
25.6M
    continue;
826
2.95M
      inside_dict = 1;
827
6.69M
      while (iter) {
828
3.73M
    next = iter->next;
829
3.73M
    if (!inside_dict)
830
776k
        xmlFree(iter);
831
3.73M
    dict->nbElems--;
832
3.73M
    inside_dict = 0;
833
3.73M
    iter = next;
834
3.73M
      }
835
2.95M
  }
836
435k
  xmlFree(dict->dict);
837
435k
    }
838
435k
    pool = dict->strings;
839
684k
    while (pool != NULL) {
840
248k
        nextp = pool->next;
841
248k
  xmlFree(pool);
842
248k
  pool = nextp;
843
248k
    }
844
435k
    xmlFree(dict);
845
435k
}
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
61.7M
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
859
61.7M
    unsigned long key, okey, nbi = 0;
860
61.7M
    xmlDictEntryPtr entry;
861
61.7M
    xmlDictEntryPtr insert;
862
61.7M
    const xmlChar *ret;
863
61.7M
    unsigned int l;
864
865
61.7M
    if ((dict == NULL) || (name == NULL))
866
174k
  return(NULL);
867
868
61.6M
    if (len < 0)
869
13.3M
        l = strlen((const char *) name);
870
48.2M
    else
871
48.2M
        l = len;
872
873
61.6M
    if (((dict->limit > 0) && (l >= dict->limit)) ||
874
61.6M
        (l > INT_MAX / 2))
875
1
        return(NULL);
876
877
    /*
878
     * Check for duplicate and insertion location.
879
     */
880
61.6M
    okey = xmlDictComputeKey(dict, name, l);
881
61.6M
    key = okey % dict->size;
882
61.6M
    if (dict->dict[key].valid == 0) {
883
2.71M
  insert = NULL;
884
58.8M
    } else {
885
75.8M
  for (insert = &(dict->dict[key]); insert->next != NULL;
886
58.8M
       insert = insert->next) {
887
31.2M
#ifdef __GNUC__
888
31.2M
      if ((insert->okey == okey) && (insert->len == l)) {
889
14.4M
    if (!memcmp(insert->name, name, l))
890
14.3M
        return(insert->name);
891
14.4M
      }
892
#else
893
      if ((insert->okey == okey) && (insert->len == l) &&
894
          (!xmlStrncmp(insert->name, name, l)))
895
    return(insert->name);
896
#endif
897
16.9M
      nbi++;
898
16.9M
  }
899
44.5M
#ifdef __GNUC__
900
44.5M
  if ((insert->okey == okey) && (insert->len == l)) {
901
43.7M
      if (!memcmp(insert->name, name, l))
902
43.6M
    return(insert->name);
903
43.7M
  }
904
#else
905
  if ((insert->okey == okey) && (insert->len == l) &&
906
      (!xmlStrncmp(insert->name, name, l)))
907
      return(insert->name);
908
#endif
909
44.5M
    }
910
911
3.56M
    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.56M
    ret = xmlDictAddString(dict, name, l);
956
3.56M
    if (ret == NULL)
957
0
        return(NULL);
958
3.56M
    if (insert == NULL) {
959
2.71M
  entry = &(dict->dict[key]);
960
2.71M
    } else {
961
849k
  entry = xmlMalloc(sizeof(xmlDictEntry));
962
849k
  if (entry == NULL)
963
0
       return(NULL);
964
849k
    }
965
3.56M
    entry->name = ret;
966
3.56M
    entry->len = l;
967
3.56M
    entry->next = NULL;
968
3.56M
    entry->valid = 1;
969
3.56M
    entry->okey = okey;
970
971
972
3.56M
    if (insert != NULL)
973
849k
  insert->next = entry;
974
975
3.56M
    dict->nbElems++;
976
977
3.56M
    if ((nbi > MAX_HASH_LEN) &&
978
6.21k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
979
4.97k
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
980
0
      return(NULL);
981
4.97k
    }
982
    /* Note that entry may have been freed at this point by xmlDictGrow */
983
984
3.56M
    return(ret);
985
3.56M
}
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
1.18M
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1107
1.18M
    unsigned long okey, key, nbi = 0;
1108
1.18M
    xmlDictEntryPtr entry;
1109
1.18M
    xmlDictEntryPtr insert;
1110
1.18M
    const xmlChar *ret;
1111
1.18M
    unsigned int len, plen, l;
1112
1113
1.18M
    if ((dict == NULL) || (name == NULL))
1114
0
  return(NULL);
1115
1.18M
    if (prefix == NULL)
1116
0
        return(xmlDictLookup(dict, name, -1));
1117
1118
1.18M
    l = len = strlen((const char *) name);
1119
1.18M
    plen = strlen((const char *) prefix);
1120
1.18M
    len += 1 + plen;
1121
1122
    /*
1123
     * Check for duplicate and insertion location.
1124
     */
1125
1.18M
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1126
1.18M
    key = okey % dict->size;
1127
1.18M
    if (dict->dict[key].valid == 0) {
1128
114k
  insert = NULL;
1129
1.07M
    } else {
1130
1.50M
  for (insert = &(dict->dict[key]); insert->next != NULL;
1131
1.07M
       insert = insert->next) {
1132
539k
      if ((insert->okey == okey) && (insert->len == len) &&
1133
151k
          (xmlStrQEqual(prefix, name, insert->name)))
1134
108k
    return(insert->name);
1135
431k
      nbi++;
1136
431k
  }
1137
965k
  if ((insert->okey == okey) && (insert->len == len) &&
1138
917k
      (xmlStrQEqual(prefix, name, insert->name)))
1139
910k
      return(insert->name);
1140
965k
    }
1141
1142
169k
    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
169k
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1172
169k
    if (ret == NULL)
1173
0
        return(NULL);
1174
169k
    if (insert == NULL) {
1175
114k
  entry = &(dict->dict[key]);
1176
114k
    } else {
1177
55.3k
  entry = xmlMalloc(sizeof(xmlDictEntry));
1178
55.3k
  if (entry == NULL)
1179
0
       return(NULL);
1180
55.3k
    }
1181
169k
    entry->name = ret;
1182
169k
    entry->len = len;
1183
169k
    entry->next = NULL;
1184
169k
    entry->valid = 1;
1185
169k
    entry->okey = okey;
1186
1187
169k
    if (insert != NULL)
1188
55.3k
  insert->next = entry;
1189
1190
169k
    dict->nbElems++;
1191
1192
169k
    if ((nbi > MAX_HASH_LEN) &&
1193
1.52k
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1194
897
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1195
    /* Note that entry may have been freed at this point by xmlDictGrow */
1196
1197
169k
    return(ret);
1198
169k
}
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
37.5M
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1212
37.5M
    xmlDictStringsPtr pool;
1213
1214
37.5M
    if ((dict == NULL) || (str == NULL))
1215
0
  return(-1);
1216
37.5M
    pool = dict->strings;
1217
58.0M
    while (pool != NULL) {
1218
49.7M
        if ((str >= &pool->array[0]) && (str <= pool->free))
1219
29.2M
      return(1);
1220
20.5M
  pool = pool->next;
1221
20.5M
    }
1222
8.29M
    if (dict->subdict)
1223
0
        return(xmlDictOwns(dict->subdict, str));
1224
8.29M
    return(0);
1225
8.29M
}
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
435k
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1257
435k
    size_t ret;
1258
1259
435k
    if (dict == NULL)
1260
0
  return(0);
1261
435k
    ret = dict->limit;
1262
435k
    dict->limit = limit;
1263
435k
    return(ret);
1264
435k
}
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"