Coverage Report

Created: 2023-06-07 06:50

/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
0
#define MAX_HASH_LEN 3
64
0
#define MIN_DICT_SIZE 128
65
0
#define MAX_DICT_HASH 100000000
66
#define WITH_BIG_KEY
67
68
#ifdef WITH_BIG_KEY
69
#define xmlDictComputeKey(dict, name, len)                              \
70
0
    (((dict)->size == MIN_DICT_SIZE) ?                                  \
71
0
     xmlDictComputeFastKey(name, len, (dict)->seed) :                   \
72
0
     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
2
int __xmlInitializeDict(void) {
161
2
    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
2
    return(1);
172
2
}
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
0
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, unsigned int namelen) {
223
0
    xmlDictStringsPtr pool;
224
0
    const xmlChar *ret;
225
0
    size_t size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
226
0
    size_t limit = 0;
227
228
#ifdef DICT_DEBUG_PATTERNS
229
    fprintf(stderr, "-");
230
#endif
231
0
    pool = dict->strings;
232
0
    while (pool != NULL) {
233
0
  if ((size_t)(pool->end - pool->free) > namelen)
234
0
      goto found_pool;
235
0
  if (pool->size > size) size = pool->size;
236
0
        limit += pool->size;
237
0
  pool = pool->next;
238
0
    }
239
    /*
240
     * Not found, need to allocate
241
     */
242
0
    if (pool == NULL) {
243
0
        if ((dict->limit > 0) && (limit > dict->limit)) {
244
0
            return(NULL);
245
0
        }
246
247
0
        if (size == 0) size = 1000;
248
0
  else size *= 4; /* exponential growth */
249
0
        if (size < 4 * namelen)
250
0
      size = 4 * namelen; /* just in case ! */
251
0
  pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
252
0
  if (pool == NULL)
253
0
      return(NULL);
254
0
  pool->size = size;
255
0
  pool->nbStrings = 0;
256
0
  pool->free = &pool->array[0];
257
0
  pool->end = &pool->array[size];
258
0
  pool->next = dict->strings;
259
0
  dict->strings = pool;
260
#ifdef DICT_DEBUG_PATTERNS
261
        fprintf(stderr, "+");
262
#endif
263
0
    }
264
0
found_pool:
265
0
    ret = pool->free;
266
0
    memcpy(pool->free, name, namelen);
267
0
    pool->free += namelen;
268
0
    *(pool->free++) = 0;
269
0
    pool->nbStrings++;
270
0
    return(ret);
271
0
}
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
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
358
#endif
359
static uint32_t
360
0
xmlDictComputeBigKey(const xmlChar* data, int namelen, int seed) {
361
0
    uint32_t hash;
362
0
    int i;
363
364
0
    if (namelen <= 0 || data == NULL) return(0);
365
366
0
    hash = seed;
367
368
0
    for (i = 0;i < namelen; i++) {
369
0
        hash += data[i];
370
0
  hash += (hash << 10);
371
0
  hash ^= (hash >> 6);
372
0
    }
373
0
    hash += (hash << 3);
374
0
    hash ^= (hash >> 11);
375
0
    hash += (hash << 15);
376
377
0
    return hash;
378
0
}
379
380
/*
381
 * xmlDictComputeBigQKey:
382
 *
383
 * Calculate a hash key for two strings using a good hash function
384
 * that works well for larger hash table sizes.
385
 *
386
 * Hash function by "One-at-a-Time Hash" see
387
 * http://burtleburtle.net/bob/hash/doobs.html
388
 *
389
 * Neither of the two strings must be NULL.
390
 */
391
#ifdef __clang__
392
ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow")
393
ATTRIBUTE_NO_SANITIZE("unsigned-shift-base")
394
#endif
395
static unsigned long
396
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
397
                      const xmlChar *name, int len, int seed)
398
0
{
399
0
    uint32_t hash;
400
0
    int i;
401
402
0
    hash = seed;
403
404
0
    for (i = 0;i < plen; i++) {
405
0
        hash += prefix[i];
406
0
  hash += (hash << 10);
407
0
  hash ^= (hash >> 6);
408
0
    }
409
0
    hash += ':';
410
0
    hash += (hash << 10);
411
0
    hash ^= (hash >> 6);
412
413
0
    for (i = 0;i < len; i++) {
414
0
        hash += name[i];
415
0
  hash += (hash << 10);
416
0
  hash ^= (hash >> 6);
417
0
    }
418
0
    hash += (hash << 3);
419
0
    hash ^= (hash >> 11);
420
0
    hash += (hash << 15);
421
422
0
    return hash;
423
0
}
424
#endif /* WITH_BIG_KEY */
425
426
/*
427
 * xmlDictComputeFastKey:
428
 *
429
 * Calculate a hash key using a fast hash function that works well
430
 * for low hash table fill.
431
 */
432
static unsigned long
433
0
xmlDictComputeFastKey(const xmlChar *name, int namelen, int seed) {
434
0
    unsigned long value = seed;
435
436
0
    if ((name == NULL) || (namelen <= 0))
437
0
        return(value);
438
0
    value += *name;
439
0
    value <<= 5;
440
0
    if (namelen > 10) {
441
0
        value += name[namelen - 1];
442
0
        namelen = 10;
443
0
    }
444
0
    switch (namelen) {
445
0
        case 10: value += name[9];
446
        /* Falls through. */
447
0
        case 9: value += name[8];
448
        /* Falls through. */
449
0
        case 8: value += name[7];
450
        /* Falls through. */
451
0
        case 7: value += name[6];
452
        /* Falls through. */
453
0
        case 6: value += name[5];
454
        /* Falls through. */
455
0
        case 5: value += name[4];
456
        /* Falls through. */
457
0
        case 4: value += name[3];
458
        /* Falls through. */
459
0
        case 3: value += name[2];
460
        /* Falls through. */
461
0
        case 2: value += name[1];
462
        /* Falls through. */
463
0
        default: break;
464
0
    }
465
0
    return(value);
466
0
}
467
468
/*
469
 * xmlDictComputeFastQKey:
470
 *
471
 * Calculate a hash key for two strings using a fast hash function
472
 * that works well for low hash table fill.
473
 *
474
 * Neither of the two strings must be NULL.
475
 */
476
static unsigned long
477
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
478
                       const xmlChar *name, int len, int seed)
479
0
{
480
0
    unsigned long value = seed;
481
482
0
    if (plen == 0)
483
0
  value += 30 * ':';
484
0
    else
485
0
  value += 30 * (*prefix);
486
487
0
    if (len > 10) {
488
0
        int offset = len - (plen + 1 + 1);
489
0
  if (offset < 0)
490
0
      offset = len - (10 + 1);
491
0
  value += name[offset];
492
0
        len = 10;
493
0
  if (plen > 10)
494
0
      plen = 10;
495
0
    }
496
0
    switch (plen) {
497
0
        case 10: value += prefix[9];
498
        /* Falls through. */
499
0
        case 9: value += prefix[8];
500
        /* Falls through. */
501
0
        case 8: value += prefix[7];
502
        /* Falls through. */
503
0
        case 7: value += prefix[6];
504
        /* Falls through. */
505
0
        case 6: value += prefix[5];
506
        /* Falls through. */
507
0
        case 5: value += prefix[4];
508
        /* Falls through. */
509
0
        case 4: value += prefix[3];
510
        /* Falls through. */
511
0
        case 3: value += prefix[2];
512
        /* Falls through. */
513
0
        case 2: value += prefix[1];
514
        /* Falls through. */
515
0
        case 1: value += prefix[0];
516
        /* Falls through. */
517
0
        default: break;
518
0
    }
519
0
    len -= plen;
520
0
    if (len > 0) {
521
0
        value += ':';
522
0
  len--;
523
0
    }
524
0
    switch (len) {
525
0
        case 10: value += name[9];
526
        /* Falls through. */
527
0
        case 9: value += name[8];
528
        /* Falls through. */
529
0
        case 8: value += name[7];
530
        /* Falls through. */
531
0
        case 7: value += name[6];
532
        /* Falls through. */
533
0
        case 6: value += name[5];
534
        /* Falls through. */
535
0
        case 5: value += name[4];
536
        /* Falls through. */
537
0
        case 4: value += name[3];
538
        /* Falls through. */
539
0
        case 3: value += name[2];
540
        /* Falls through. */
541
0
        case 2: value += name[1];
542
        /* Falls through. */
543
0
        case 1: value += name[0];
544
        /* Falls through. */
545
0
        default: break;
546
0
    }
547
0
    return(value);
548
0
}
549
550
/**
551
 * xmlDictCreate:
552
 *
553
 * Create a new dictionary
554
 *
555
 * Returns the newly created dictionary, or NULL if an error occurred.
556
 */
557
xmlDictPtr
558
0
xmlDictCreate(void) {
559
0
    xmlDictPtr dict;
560
561
0
    xmlInitParser();
562
563
#ifdef DICT_DEBUG_PATTERNS
564
    fprintf(stderr, "C");
565
#endif
566
567
0
    dict = xmlMalloc(sizeof(xmlDict));
568
0
    if (dict) {
569
0
        dict->ref_counter = 1;
570
0
        dict->limit = 0;
571
572
0
        dict->size = MIN_DICT_SIZE;
573
0
  dict->nbElems = 0;
574
0
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
575
0
  dict->strings = NULL;
576
0
  dict->subdict = NULL;
577
0
        if (dict->dict) {
578
0
      memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
579
#ifdef DICT_RANDOMIZATION
580
            dict->seed = __xmlRandom();
581
#else
582
0
            dict->seed = 0;
583
0
#endif
584
0
      return(dict);
585
0
        }
586
0
        xmlFree(dict);
587
0
    }
588
0
    return(NULL);
589
0
}
590
591
/**
592
 * xmlDictCreateSub:
593
 * @sub: an existing dictionary
594
 *
595
 * Create a new dictionary, inheriting strings from the read-only
596
 * dictionary @sub. On lookup, strings are first searched in the
597
 * new dictionary, then in @sub, and if not found are created in the
598
 * new dictionary.
599
 *
600
 * Returns the newly created dictionary, or NULL if an error occurred.
601
 */
602
xmlDictPtr
603
0
xmlDictCreateSub(xmlDictPtr sub) {
604
0
    xmlDictPtr dict = xmlDictCreate();
605
606
0
    if ((dict != NULL) && (sub != NULL)) {
607
#ifdef DICT_DEBUG_PATTERNS
608
        fprintf(stderr, "R");
609
#endif
610
0
        dict->seed = sub->seed;
611
0
        dict->subdict = sub;
612
0
  xmlDictReference(dict->subdict);
613
0
    }
614
0
    return(dict);
615
0
}
616
617
/**
618
 * xmlDictReference:
619
 * @dict: the dictionary
620
 *
621
 * Increment the reference counter of a dictionary
622
 *
623
 * Returns 0 in case of success and -1 in case of error
624
 */
625
int
626
0
xmlDictReference(xmlDictPtr dict) {
627
0
    if (dict == NULL) return -1;
628
0
    xmlMutexLock(&xmlDictMutex);
629
0
    dict->ref_counter++;
630
0
    xmlMutexUnlock(&xmlDictMutex);
631
0
    return(0);
632
0
}
633
634
/**
635
 * xmlDictGrow:
636
 * @dict: the dictionary
637
 * @size: the new size of the dictionary
638
 *
639
 * resize the dictionary
640
 *
641
 * Returns 0 in case of success, -1 in case of failure
642
 */
643
static int
644
0
xmlDictGrow(xmlDictPtr dict, size_t size) {
645
0
    unsigned long key, okey;
646
0
    size_t oldsize, i;
647
0
    xmlDictEntryPtr iter, next;
648
0
    struct _xmlDictEntry *olddict;
649
#ifdef DEBUG_GROW
650
    unsigned long nbElem = 0;
651
#endif
652
0
    int ret = 0;
653
0
    int keep_keys = 1;
654
655
0
    if (dict == NULL)
656
0
  return(-1);
657
0
    if (size < 8)
658
0
        return(-1);
659
0
    if (size > MAX_DICT_HASH)
660
0
  return(-1);
661
662
#ifdef DICT_DEBUG_PATTERNS
663
    fprintf(stderr, "*");
664
#endif
665
666
0
    oldsize = dict->size;
667
0
    olddict = dict->dict;
668
0
    if (olddict == NULL)
669
0
        return(-1);
670
0
    if (oldsize == MIN_DICT_SIZE)
671
0
        keep_keys = 0;
672
673
0
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
674
0
    if (dict->dict == NULL) {
675
0
  dict->dict = olddict;
676
0
  return(-1);
677
0
    }
678
0
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
679
0
    dict->size = size;
680
681
    /*  If the two loops are merged, there would be situations where
682
  a new entry needs to allocated and data copied into it from
683
  the main dict. It is nicer to run through the array twice, first
684
  copying all the elements in the main array (less probability of
685
  allocate) and then the rest, so we only free in the second loop.
686
    */
687
0
    for (i = 0; i < oldsize; i++) {
688
0
  if (olddict[i].valid == 0)
689
0
      continue;
690
691
0
  if (keep_keys)
692
0
      okey = olddict[i].okey;
693
0
  else
694
0
      okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
695
0
  key = okey % dict->size;
696
697
0
  if (dict->dict[key].valid == 0) {
698
0
      memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
699
0
      dict->dict[key].next = NULL;
700
0
      dict->dict[key].okey = okey;
701
0
  } else {
702
0
      xmlDictEntryPtr entry;
703
704
0
      entry = xmlMalloc(sizeof(xmlDictEntry));
705
0
      if (entry != NULL) {
706
0
    entry->name = olddict[i].name;
707
0
    entry->len = olddict[i].len;
708
0
    entry->okey = okey;
709
0
    entry->next = dict->dict[key].next;
710
0
    entry->valid = 1;
711
0
    dict->dict[key].next = entry;
712
0
      } else {
713
          /*
714
     * we don't have much ways to alert from here
715
     * result is losing an entry and unicity guarantee
716
     */
717
0
          ret = -1;
718
0
      }
719
0
  }
720
#ifdef DEBUG_GROW
721
  nbElem++;
722
#endif
723
0
    }
724
725
0
    for (i = 0; i < oldsize; i++) {
726
0
  iter = olddict[i].next;
727
0
  while (iter) {
728
0
      next = iter->next;
729
730
      /*
731
       * put back the entry in the new dict
732
       */
733
734
0
      if (keep_keys)
735
0
    okey = iter->okey;
736
0
      else
737
0
    okey = xmlDictComputeKey(dict, iter->name, iter->len);
738
0
      key = okey % dict->size;
739
0
      if (dict->dict[key].valid == 0) {
740
0
    memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
741
0
    dict->dict[key].next = NULL;
742
0
    dict->dict[key].valid = 1;
743
0
    dict->dict[key].okey = okey;
744
0
    xmlFree(iter);
745
0
      } else {
746
0
    iter->next = dict->dict[key].next;
747
0
    iter->okey = okey;
748
0
    dict->dict[key].next = iter;
749
0
      }
750
751
#ifdef DEBUG_GROW
752
      nbElem++;
753
#endif
754
755
0
      iter = next;
756
0
  }
757
0
    }
758
759
0
    xmlFree(olddict);
760
761
#ifdef DEBUG_GROW
762
    xmlGenericError(xmlGenericErrorContext,
763
      "xmlDictGrow : from %lu to %lu, %u elems\n", oldsize, size, nbElem);
764
#endif
765
766
0
    return(ret);
767
0
}
768
769
/**
770
 * xmlDictFree:
771
 * @dict: the dictionary
772
 *
773
 * Free the hash @dict and its contents. The userdata is
774
 * deallocated with @f if provided.
775
 */
776
void
777
0
xmlDictFree(xmlDictPtr dict) {
778
0
    size_t i;
779
0
    xmlDictEntryPtr iter;
780
0
    xmlDictEntryPtr next;
781
0
    int inside_dict = 0;
782
0
    xmlDictStringsPtr pool, nextp;
783
784
0
    if (dict == NULL)
785
0
  return;
786
787
    /* decrement the counter, it may be shared by a parser and docs */
788
0
    xmlMutexLock(&xmlDictMutex);
789
0
    dict->ref_counter--;
790
0
    if (dict->ref_counter > 0) {
791
0
        xmlMutexUnlock(&xmlDictMutex);
792
0
        return;
793
0
    }
794
795
0
    xmlMutexUnlock(&xmlDictMutex);
796
797
0
    if (dict->subdict != NULL) {
798
0
        xmlDictFree(dict->subdict);
799
0
    }
800
801
0
    if (dict->dict) {
802
0
  for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
803
0
      iter = &(dict->dict[i]);
804
0
      if (iter->valid == 0)
805
0
    continue;
806
0
      inside_dict = 1;
807
0
      while (iter) {
808
0
    next = iter->next;
809
0
    if (!inside_dict)
810
0
        xmlFree(iter);
811
0
    dict->nbElems--;
812
0
    inside_dict = 0;
813
0
    iter = next;
814
0
      }
815
0
  }
816
0
  xmlFree(dict->dict);
817
0
    }
818
0
    pool = dict->strings;
819
0
    while (pool != NULL) {
820
0
        nextp = pool->next;
821
0
  xmlFree(pool);
822
0
  pool = nextp;
823
0
    }
824
0
    xmlFree(dict);
825
0
}
826
827
/**
828
 * xmlDictLookup:
829
 * @dict: the dictionary
830
 * @name: the name of the userdata
831
 * @len: the length of the name, if -1 it is recomputed
832
 *
833
 * Add the @name to the dictionary @dict if not present.
834
 *
835
 * Returns the internal copy of the name or NULL in case of internal error
836
 */
837
const xmlChar *
838
0
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
839
0
    unsigned long key, okey, nbi = 0;
840
0
    xmlDictEntryPtr entry;
841
0
    xmlDictEntryPtr insert;
842
0
    const xmlChar *ret;
843
0
    unsigned int l;
844
845
0
    if ((dict == NULL) || (name == NULL))
846
0
  return(NULL);
847
848
0
    if (len < 0)
849
0
        l = strlen((const char *) name);
850
0
    else
851
0
        l = len;
852
853
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
854
0
        (l > INT_MAX / 2))
855
0
        return(NULL);
856
857
    /*
858
     * Check for duplicate and insertion location.
859
     */
860
0
    okey = xmlDictComputeKey(dict, name, l);
861
0
    key = okey % dict->size;
862
0
    if (dict->dict[key].valid == 0) {
863
0
  insert = NULL;
864
0
    } else {
865
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
866
0
       insert = insert->next) {
867
0
#ifdef __GNUC__
868
0
      if ((insert->okey == okey) && (insert->len == l)) {
869
0
    if (!memcmp(insert->name, name, l))
870
0
        return(insert->name);
871
0
      }
872
#else
873
      if ((insert->okey == okey) && (insert->len == l) &&
874
          (!xmlStrncmp(insert->name, name, l)))
875
    return(insert->name);
876
#endif
877
0
      nbi++;
878
0
  }
879
0
#ifdef __GNUC__
880
0
  if ((insert->okey == okey) && (insert->len == l)) {
881
0
      if (!memcmp(insert->name, name, l))
882
0
    return(insert->name);
883
0
  }
884
#else
885
  if ((insert->okey == okey) && (insert->len == l) &&
886
      (!xmlStrncmp(insert->name, name, l)))
887
      return(insert->name);
888
#endif
889
0
    }
890
891
0
    if (dict->subdict) {
892
0
        unsigned long skey;
893
894
        /* we cannot always reuse the same okey for the subdict */
895
0
        if (((dict->size == MIN_DICT_SIZE) &&
896
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
897
0
            ((dict->size != MIN_DICT_SIZE) &&
898
0
       (dict->subdict->size == MIN_DICT_SIZE)))
899
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
900
0
  else
901
0
      skey = okey;
902
903
0
  key = skey % dict->subdict->size;
904
0
  if (dict->subdict->dict[key].valid != 0) {
905
0
      xmlDictEntryPtr tmp;
906
907
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
908
0
     tmp = tmp->next) {
909
0
#ifdef __GNUC__
910
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
911
0
        if (!memcmp(tmp->name, name, l))
912
0
      return(tmp->name);
913
0
    }
914
#else
915
    if ((tmp->okey == skey) && (tmp->len == l) &&
916
        (!xmlStrncmp(tmp->name, name, l)))
917
        return(tmp->name);
918
#endif
919
0
    nbi++;
920
0
      }
921
0
#ifdef __GNUC__
922
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
923
0
    if (!memcmp(tmp->name, name, l))
924
0
        return(tmp->name);
925
0
      }
926
#else
927
      if ((tmp->okey == skey) && (tmp->len == l) &&
928
    (!xmlStrncmp(tmp->name, name, l)))
929
    return(tmp->name);
930
#endif
931
0
  }
932
0
  key = okey % dict->size;
933
0
    }
934
935
0
    ret = xmlDictAddString(dict, name, l);
936
0
    if (ret == NULL)
937
0
        return(NULL);
938
0
    if (insert == NULL) {
939
0
  entry = &(dict->dict[key]);
940
0
    } else {
941
0
  entry = xmlMalloc(sizeof(xmlDictEntry));
942
0
  if (entry == NULL)
943
0
       return(NULL);
944
0
    }
945
0
    entry->name = ret;
946
0
    entry->len = l;
947
0
    entry->next = NULL;
948
0
    entry->valid = 1;
949
0
    entry->okey = okey;
950
951
952
0
    if (insert != NULL)
953
0
  insert->next = entry;
954
955
0
    dict->nbElems++;
956
957
0
    if ((nbi > MAX_HASH_LEN) &&
958
0
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
959
0
  if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
960
0
      return(NULL);
961
0
    }
962
    /* Note that entry may have been freed at this point by xmlDictGrow */
963
964
0
    return(ret);
965
0
}
966
967
/**
968
 * xmlDictExists:
969
 * @dict: the dictionary
970
 * @name: the name of the userdata
971
 * @len: the length of the name, if -1 it is recomputed
972
 *
973
 * Check if the @name exists in the dictionary @dict.
974
 *
975
 * Returns the internal copy of the name or NULL if not found.
976
 */
977
const xmlChar *
978
0
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
979
0
    unsigned long key, okey;
980
0
    xmlDictEntryPtr insert;
981
0
    unsigned int l;
982
983
0
    if ((dict == NULL) || (name == NULL))
984
0
  return(NULL);
985
986
0
    if (len < 0)
987
0
        l = strlen((const char *) name);
988
0
    else
989
0
        l = len;
990
0
    if (((dict->limit > 0) && (l >= dict->limit)) ||
991
0
        (l > INT_MAX / 2))
992
0
        return(NULL);
993
994
    /*
995
     * Check for duplicate and insertion location.
996
     */
997
0
    okey = xmlDictComputeKey(dict, name, l);
998
0
    key = okey % dict->size;
999
0
    if (dict->dict[key].valid == 0) {
1000
0
  insert = NULL;
1001
0
    } else {
1002
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1003
0
       insert = insert->next) {
1004
0
#ifdef __GNUC__
1005
0
      if ((insert->okey == okey) && (insert->len == l)) {
1006
0
    if (!memcmp(insert->name, name, l))
1007
0
        return(insert->name);
1008
0
      }
1009
#else
1010
      if ((insert->okey == okey) && (insert->len == l) &&
1011
          (!xmlStrncmp(insert->name, name, l)))
1012
    return(insert->name);
1013
#endif
1014
0
  }
1015
0
#ifdef __GNUC__
1016
0
  if ((insert->okey == okey) && (insert->len == l)) {
1017
0
      if (!memcmp(insert->name, name, l))
1018
0
    return(insert->name);
1019
0
  }
1020
#else
1021
  if ((insert->okey == okey) && (insert->len == l) &&
1022
      (!xmlStrncmp(insert->name, name, l)))
1023
      return(insert->name);
1024
#endif
1025
0
    }
1026
1027
0
    if (dict->subdict) {
1028
0
        unsigned long skey;
1029
1030
        /* we cannot always reuse the same okey for the subdict */
1031
0
        if (((dict->size == MIN_DICT_SIZE) &&
1032
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1033
0
            ((dict->size != MIN_DICT_SIZE) &&
1034
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1035
0
      skey = xmlDictComputeKey(dict->subdict, name, l);
1036
0
  else
1037
0
      skey = okey;
1038
1039
0
  key = skey % dict->subdict->size;
1040
0
  if (dict->subdict->dict[key].valid != 0) {
1041
0
      xmlDictEntryPtr tmp;
1042
1043
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1044
0
     tmp = tmp->next) {
1045
0
#ifdef __GNUC__
1046
0
    if ((tmp->okey == skey) && (tmp->len == l)) {
1047
0
        if (!memcmp(tmp->name, name, l))
1048
0
      return(tmp->name);
1049
0
    }
1050
#else
1051
    if ((tmp->okey == skey) && (tmp->len == l) &&
1052
        (!xmlStrncmp(tmp->name, name, l)))
1053
        return(tmp->name);
1054
#endif
1055
0
      }
1056
0
#ifdef __GNUC__
1057
0
      if ((tmp->okey == skey) && (tmp->len == l)) {
1058
0
    if (!memcmp(tmp->name, name, l))
1059
0
        return(tmp->name);
1060
0
      }
1061
#else
1062
      if ((tmp->okey == skey) && (tmp->len == l) &&
1063
    (!xmlStrncmp(tmp->name, name, l)))
1064
    return(tmp->name);
1065
#endif
1066
0
  }
1067
0
    }
1068
1069
    /* not found */
1070
0
    return(NULL);
1071
0
}
1072
1073
/**
1074
 * xmlDictQLookup:
1075
 * @dict: the dictionary
1076
 * @prefix: the prefix
1077
 * @name: the name
1078
 *
1079
 * Add the QName @prefix:@name to the hash @dict if not present.
1080
 *
1081
 * Returns the internal copy of the QName or NULL in case of internal error
1082
 */
1083
const xmlChar *
1084
0
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
1085
0
    unsigned long okey, key, nbi = 0;
1086
0
    xmlDictEntryPtr entry;
1087
0
    xmlDictEntryPtr insert;
1088
0
    const xmlChar *ret;
1089
0
    unsigned int len, plen, l;
1090
1091
0
    if ((dict == NULL) || (name == NULL))
1092
0
  return(NULL);
1093
0
    if (prefix == NULL)
1094
0
        return(xmlDictLookup(dict, name, -1));
1095
1096
0
    l = len = strlen((const char *) name);
1097
0
    plen = strlen((const char *) prefix);
1098
0
    len += 1 + plen;
1099
1100
    /*
1101
     * Check for duplicate and insertion location.
1102
     */
1103
0
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
1104
0
    key = okey % dict->size;
1105
0
    if (dict->dict[key].valid == 0) {
1106
0
  insert = NULL;
1107
0
    } else {
1108
0
  for (insert = &(dict->dict[key]); insert->next != NULL;
1109
0
       insert = insert->next) {
1110
0
      if ((insert->okey == okey) && (insert->len == len) &&
1111
0
          (xmlStrQEqual(prefix, name, insert->name)))
1112
0
    return(insert->name);
1113
0
      nbi++;
1114
0
  }
1115
0
  if ((insert->okey == okey) && (insert->len == len) &&
1116
0
      (xmlStrQEqual(prefix, name, insert->name)))
1117
0
      return(insert->name);
1118
0
    }
1119
1120
0
    if (dict->subdict) {
1121
0
        unsigned long skey;
1122
1123
        /* we cannot always reuse the same okey for the subdict */
1124
0
        if (((dict->size == MIN_DICT_SIZE) &&
1125
0
       (dict->subdict->size != MIN_DICT_SIZE)) ||
1126
0
            ((dict->size != MIN_DICT_SIZE) &&
1127
0
       (dict->subdict->size == MIN_DICT_SIZE)))
1128
0
      skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1129
0
  else
1130
0
      skey = okey;
1131
1132
0
  key = skey % dict->subdict->size;
1133
0
  if (dict->subdict->dict[key].valid != 0) {
1134
0
      xmlDictEntryPtr tmp;
1135
0
      for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1136
0
     tmp = tmp->next) {
1137
0
    if ((tmp->okey == skey) && (tmp->len == len) &&
1138
0
        (xmlStrQEqual(prefix, name, tmp->name)))
1139
0
        return(tmp->name);
1140
0
    nbi++;
1141
0
      }
1142
0
      if ((tmp->okey == skey) && (tmp->len == len) &&
1143
0
    (xmlStrQEqual(prefix, name, tmp->name)))
1144
0
    return(tmp->name);
1145
0
  }
1146
0
  key = okey % dict->size;
1147
0
    }
1148
1149
0
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1150
0
    if (ret == NULL)
1151
0
        return(NULL);
1152
0
    if (insert == NULL) {
1153
0
  entry = &(dict->dict[key]);
1154
0
    } else {
1155
0
  entry = xmlMalloc(sizeof(xmlDictEntry));
1156
0
  if (entry == NULL)
1157
0
       return(NULL);
1158
0
    }
1159
0
    entry->name = ret;
1160
0
    entry->len = len;
1161
0
    entry->next = NULL;
1162
0
    entry->valid = 1;
1163
0
    entry->okey = okey;
1164
1165
0
    if (insert != NULL)
1166
0
  insert->next = entry;
1167
1168
0
    dict->nbElems++;
1169
1170
0
    if ((nbi > MAX_HASH_LEN) &&
1171
0
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1172
0
  xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1173
    /* Note that entry may have been freed at this point by xmlDictGrow */
1174
1175
0
    return(ret);
1176
0
}
1177
1178
/**
1179
 * xmlDictOwns:
1180
 * @dict: the dictionary
1181
 * @str: the string
1182
 *
1183
 * check if a string is owned by the dictionary
1184
 *
1185
 * Returns 1 if true, 0 if false and -1 in case of error
1186
 * -1 in case of error
1187
 */
1188
int
1189
0
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1190
0
    xmlDictStringsPtr pool;
1191
1192
0
    if ((dict == NULL) || (str == NULL))
1193
0
  return(-1);
1194
0
    pool = dict->strings;
1195
0
    while (pool != NULL) {
1196
0
        if ((str >= &pool->array[0]) && (str <= pool->free))
1197
0
      return(1);
1198
0
  pool = pool->next;
1199
0
    }
1200
0
    if (dict->subdict)
1201
0
        return(xmlDictOwns(dict->subdict, str));
1202
0
    return(0);
1203
0
}
1204
1205
/**
1206
 * xmlDictSize:
1207
 * @dict: the dictionary
1208
 *
1209
 * Query the number of elements installed in the hash @dict.
1210
 *
1211
 * Returns the number of elements in the dictionary or
1212
 * -1 in case of error
1213
 */
1214
int
1215
0
xmlDictSize(xmlDictPtr dict) {
1216
0
    if (dict == NULL)
1217
0
  return(-1);
1218
0
    if (dict->subdict)
1219
0
        return(dict->nbElems + dict->subdict->nbElems);
1220
0
    return(dict->nbElems);
1221
0
}
1222
1223
/**
1224
 * xmlDictSetLimit:
1225
 * @dict: the dictionary
1226
 * @limit: the limit in bytes
1227
 *
1228
 * Set a size limit for the dictionary
1229
 * Added in 2.9.0
1230
 *
1231
 * Returns the previous limit of the dictionary or 0
1232
 */
1233
size_t
1234
0
xmlDictSetLimit(xmlDictPtr dict, size_t limit) {
1235
0
    size_t ret;
1236
1237
0
    if (dict == NULL)
1238
0
  return(0);
1239
0
    ret = dict->limit;
1240
0
    dict->limit = limit;
1241
0
    return(ret);
1242
0
}
1243
1244
/**
1245
 * xmlDictGetUsage:
1246
 * @dict: the dictionary
1247
 *
1248
 * Get how much memory is used by a dictionary for strings
1249
 * Added in 2.9.0
1250
 *
1251
 * Returns the amount of strings allocated
1252
 */
1253
size_t
1254
0
xmlDictGetUsage(xmlDictPtr dict) {
1255
0
    xmlDictStringsPtr pool;
1256
0
    size_t limit = 0;
1257
1258
0
    if (dict == NULL)
1259
0
  return(0);
1260
0
    pool = dict->strings;
1261
0
    while (pool != NULL) {
1262
0
        limit += pool->size;
1263
0
  pool = pool->next;
1264
0
    }
1265
0
    return(limit);
1266
0
}
1267