Coverage Report

Created: 2025-08-04 07:15

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