Coverage Report

Created: 2024-08-27 12:19

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