1
/*
2
 * dict.c: dictionary of reusable strings, just used to avoid allocation
3
 *         and freeing operations.
4
 *
5
 * Copyright (C) 2003 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 <string.h>
23
#ifdef HAVE_STDINT_H
24
#include <stdint.h>
25
#else
26
#ifdef HAVE_INTTYPES_H
27
#include <inttypes.h>
28
#elif defined(WIN32)
29
typedef unsigned __int32 uint32_t;
30
#endif
31
#endif
32
#include <libxml/tree.h>
33
#include <libxml/dict.h>
34
#include <libxml/xmlmemory.h>
35
#include <libxml/xmlerror.h>
36
#include <libxml/globals.h>
37
38
/* #define DEBUG_GROW */
39
/* #define DICT_DEBUG_PATTERNS */
40
41
#define MAX_HASH_LEN 3
42
#define MIN_DICT_SIZE 128
43
#define MAX_DICT_HASH 8 * 2048
44
#define WITH_BIG_KEY
45
46
#ifdef WITH_BIG_KEY
47
#define xmlDictComputeKey(dict, name, len)			\
48
    (((dict)->size == MIN_DICT_SIZE) ?				\
49
     xmlDictComputeFastKey(name, len) :				\
50
     xmlDictComputeBigKey(name, len))
51
52
#define xmlDictComputeQKey(dict, prefix, plen, name, len)	\
53
    (((prefix) == NULL) ?					\
54
      (xmlDictComputeKey(dict, name, len)) :			\
55
      (((dict)->size == MIN_DICT_SIZE) ?			\
56
       xmlDictComputeFastQKey(prefix, plen, name, len) :	\
57
       xmlDictComputeBigQKey(prefix, plen, name, len)))
58
59
#else /* !WITH_BIG_KEY */
60
#define xmlDictComputeKey(dict, name, len)			\
61
        xmlDictComputeFastKey(name, len)
62
#define xmlDictComputeQKey(dict, prefix, plen, name, len)	\
63
        xmlDictComputeFastQKey(prefix, plen, name, len)
64
#endif /* WITH_BIG_KEY */
65
66
/*
67
 * An entry in the dictionnary
68
 */
69
typedef struct _xmlDictEntry xmlDictEntry;
70
typedef xmlDictEntry *xmlDictEntryPtr;
71
struct _xmlDictEntry {
72
    struct _xmlDictEntry *next;
73
    const xmlChar *name;
74
    int len;
75
    int valid;
76
    unsigned long okey;
77
};
78
79
typedef struct _xmlDictStrings xmlDictStrings;
80
typedef xmlDictStrings *xmlDictStringsPtr;
81
struct _xmlDictStrings {
82
    xmlDictStringsPtr next;
83
    xmlChar *free;
84
    xmlChar *end;
85
    int size;
86
    int nbStrings;
87
    xmlChar array[1];
88
};
89
/*
90
 * The entire dictionnary
91
 */
92
struct _xmlDict {
93
    int ref_counter;
94
95
    struct _xmlDictEntry *dict;
96
    int size;
97
    int nbElems;
98
    xmlDictStringsPtr strings;
99
100
    struct _xmlDict *subdict;
101
};
102
103
/*
104
 * A mutex for modifying the reference counter for shared
105
 * dictionaries.
106
 */
107
static xmlRMutexPtr xmlDictMutex = NULL;
108
109
/*
110
 * Whether the dictionary mutex was initialized.
111
 */
112
static int xmlDictInitialized = 0;
113
114
/**
115
 * xmlInitializeDict:
116
 *
117
 * Do the dictionary mutex initialization.
118
 * this function is not thread safe, initialization should
119
 * preferably be done once at startup
120
 */
121
static int xmlInitializeDict(void) {
122
    if (xmlDictInitialized)
123
        return(1);
124
125
    if ((xmlDictMutex = xmlNewRMutex()) == NULL)
126
        return(0);
127
128
    xmlDictInitialized = 1;
129
    return(1);
130
}
131
132
/**
133
 * xmlDictCleanup:
134
 *
135
 * Free the dictionary mutex.
136
 */
137
void
138
xmlDictCleanup(void) {
139
    if (!xmlDictInitialized)
140
        return;
141
142
    xmlFreeRMutex(xmlDictMutex);
143
144
    xmlDictInitialized = 0;
145
}
146
147
/*
148
 * xmlDictAddString:
149
 * @dict: the dictionnary
150
 * @name: the name of the userdata
151
 * @len: the length of the name, if -1 it is recomputed
152
 *
153
 * Add the string to the array[s]
154
 *
155
 * Returns the pointer of the local string, or NULL in case of error.
156
 */
157
static const xmlChar *
158
xmlDictAddString(xmlDictPtr dict, const xmlChar *name, int namelen) {
159
    xmlDictStringsPtr pool;
160
    const xmlChar *ret;
161
    int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
162
163
#ifdef DICT_DEBUG_PATTERNS
164
    fprintf(stderr, "-");
165
#endif
166
    pool = dict->strings;
167
    while (pool != NULL) {
168
	if (pool->end - pool->free > namelen)
169
	    goto found_pool;
170
	if (pool->size > size) size = pool->size;
171
	pool = pool->next;
172
    }
173
    /*
174
     * Not found, need to allocate
175
     */
176
    if (pool == NULL) {
177
        if (size == 0) size = 1000;
178
	else size *= 4; /* exponential growth */
179
        if (size < 4 * namelen) 
180
	    size = 4 * namelen; /* just in case ! */
181
	pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
182
	if (pool == NULL)
183
	    return(NULL);
184
	pool->size = size;
185
	pool->nbStrings = 0;
186
	pool->free = &pool->array[0];
187
	pool->end = &pool->array[size];
188
	pool->next = dict->strings;
189
	dict->strings = pool;
190
#ifdef DICT_DEBUG_PATTERNS
191
        fprintf(stderr, "+");
192
#endif
193
    }
194
found_pool:
195
    ret = pool->free;
196
    memcpy(pool->free, name, namelen);
197
    pool->free += namelen;
198
    *(pool->free++) = 0;
199
    pool->nbStrings++;
200
    return(ret);
201
}
202
203
/*
204
 * xmlDictAddQString:
205
 * @dict: the dictionnary
206
 * @prefix: the prefix of the userdata
207
 * @plen: the prefix length
208
 * @name: the name of the userdata
209
 * @len: the length of the name, if -1 it is recomputed
210
 *
211
 * Add the QName to the array[s]
212
 *
213
 * Returns the pointer of the local string, or NULL in case of error.
214
 */
215
static const xmlChar *
216
xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, int plen,
217
                 const xmlChar *name, int namelen)
218
{
219
    xmlDictStringsPtr pool;
220
    const xmlChar *ret;
221
    int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
222
223
    if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
224
225
#ifdef DICT_DEBUG_PATTERNS
226
    fprintf(stderr, "=");
227
#endif
228
    pool = dict->strings;
229
    while (pool != NULL) {
230
	if (pool->end - pool->free > namelen + plen + 1)
231
	    goto found_pool;
232
	if (pool->size > size) size = pool->size;
233
	pool = pool->next;
234
    }
235
    /*
236
     * Not found, need to allocate
237
     */
238
    if (pool == NULL) {
239
        if (size == 0) size = 1000;
240
	else size *= 4; /* exponential growth */
241
        if (size < 4 * (namelen + plen + 1))
242
	    size = 4 * (namelen + plen + 1); /* just in case ! */
243
	pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
244
	if (pool == NULL)
245
	    return(NULL);
246
	pool->size = size;
247
	pool->nbStrings = 0;
248
	pool->free = &pool->array[0];
249
	pool->end = &pool->array[size];
250
	pool->next = dict->strings;
251
	dict->strings = pool;
252
#ifdef DICT_DEBUG_PATTERNS
253
        fprintf(stderr, "+");
254
#endif
255
    }
256
found_pool:
257
    ret = pool->free;
258
    memcpy(pool->free, prefix, plen);
259
    pool->free += plen;
260
    *(pool->free++) = ':';
261
    memcpy(pool->free, name, namelen);
262
    pool->free += namelen;
263
    *(pool->free++) = 0;
264
    pool->nbStrings++;
265
    return(ret);
266
}
267
268
#ifdef WITH_BIG_KEY
269
/*
270
 * xmlDictComputeBigKey:
271
 *
272
 * Calculate a hash key using a good hash function that works well for
273
 * larger hash table sizes.
274
 *
275
 * Hash function by "One-at-a-Time Hash" see
276
 * http://burtleburtle.net/bob/hash/doobs.html
277
 */
278
279
static uint32_t
280
xmlDictComputeBigKey(const xmlChar* data, int namelen) {
281
    uint32_t hash;
282
    int i;
283
284
    if (namelen <= 0 || data == NULL) return(0);
285
286
    hash = 0;
287
288
    for (i = 0;i < namelen; i++) {
289
        hash += data[i];
290
	hash += (hash << 10);
291
	hash ^= (hash >> 6);
292
    }
293
    hash += (hash << 3);
294
    hash ^= (hash >> 11);
295
    hash += (hash << 15);
296
297
    return hash;
298
}
299
300
/*
301
 * xmlDictComputeBigQKey:
302
 *
303
 * Calculate a hash key for two strings using a good hash function
304
 * that works well for larger hash table sizes.
305
 *
306
 * Hash function by "One-at-a-Time Hash" see
307
 * http://burtleburtle.net/bob/hash/doobs.html
308
 *
309
 * Neither of the two strings must be NULL.
310
 */
311
static unsigned long
312
xmlDictComputeBigQKey(const xmlChar *prefix, int plen,
313
                      const xmlChar *name, int len)
314
{
315
    uint32_t hash;
316
    int i;
317
318
    hash = 0;
319
320
    for (i = 0;i < plen; i++) {
321
        hash += prefix[i];
322
	hash += (hash << 10);
323
	hash ^= (hash >> 6);
324
    }
325
    hash += ':';
326
    hash += (hash << 10);
327
    hash ^= (hash >> 6);
328
329
    for (i = 0;i < len; i++) {
330
        hash += name[i];
331
	hash += (hash << 10);
332
	hash ^= (hash >> 6);
333
    }
334
    hash += (hash << 3);
335
    hash ^= (hash >> 11);
336
    hash += (hash << 15);
337
338
    return hash;
339
}
340
#endif /* WITH_BIG_KEY */
341
342
/*
343
 * xmlDictComputeFastKey:
344
 *
345
 * Calculate a hash key using a fast hash function that works well
346
 * for low hash table fill.
347
 */
348
static unsigned long
349
xmlDictComputeFastKey(const xmlChar *name, int namelen) {
350
    unsigned long value = 0L;
351
352
    if (name == NULL) return(0);
353
    value = *name;
354
    value <<= 5;
355
    if (namelen > 10) {
356
        value += name[namelen - 1];
357
        namelen = 10;
358
    }
359
    switch (namelen) {
360
        case 10: value += name[9];
361
        case 9: value += name[8];
362
        case 8: value += name[7];
363
        case 7: value += name[6];
364
        case 6: value += name[5];
365
        case 5: value += name[4];
366
        case 4: value += name[3];
367
        case 3: value += name[2];
368
        case 2: value += name[1];
369
        default: break;
370
    }
371
    return(value);
372
}
373
374
/*
375
 * xmlDictComputeFastQKey:
376
 *
377
 * Calculate a hash key for two strings using a fast hash function
378
 * that works well for low hash table fill.
379
 *
380
 * Neither of the two strings must be NULL.
381
 */
382
static unsigned long
383
xmlDictComputeFastQKey(const xmlChar *prefix, int plen,
384
                       const xmlChar *name, int len)
385
{
386
    unsigned long value = 0L;
387
388
    if (plen == 0)
389
	value += 30 * (unsigned long) ':';
390
    else
391
	value += 30 * (*prefix);
392
393
    if (len > 10) {
394
        value += name[len - (plen + 1 + 1)];
395
        len = 10;
396
	if (plen > 10)
397
	    plen = 10;
398
    }
399
    switch (plen) {
400
        case 10: value += prefix[9];
401
        case 9: value += prefix[8];
402
        case 8: value += prefix[7];
403
        case 7: value += prefix[6];
404
        case 6: value += prefix[5];
405
        case 5: value += prefix[4];
406
        case 4: value += prefix[3];
407
        case 3: value += prefix[2];
408
        case 2: value += prefix[1];
409
        case 1: value += prefix[0];
410
        default: break;
411
    }
412
    len -= plen;
413
    if (len > 0) {
414
        value += (unsigned long) ':';
415
	len--;
416
    }
417
    switch (len) {
418
        case 10: value += name[9];
419
        case 9: value += name[8];
420
        case 8: value += name[7];
421
        case 7: value += name[6];
422
        case 6: value += name[5];
423
        case 5: value += name[4];
424
        case 4: value += name[3];
425
        case 3: value += name[2];
426
        case 2: value += name[1];
427
        case 1: value += name[0];
428
        default: break;
429
    }
430
    return(value);
431
}
432
433
/**
434
 * xmlDictCreate:
435
 *
436
 * Create a new dictionary
437
 *
438
 * Returns the newly created dictionnary, or NULL if an error occured.
439
 */
440
xmlDictPtr
441
xmlDictCreate(void) {
442
    xmlDictPtr dict;
443
444
    if (!xmlDictInitialized)
445
        if (!xmlInitializeDict())
446
            return(NULL);
447
448
#ifdef DICT_DEBUG_PATTERNS
449
    fprintf(stderr, "C");
450
#endif
451
452
    dict = xmlMalloc(sizeof(xmlDict));
453
    if (dict) {
454
        dict->ref_counter = 1;
455
456
        dict->size = MIN_DICT_SIZE;
457
	dict->nbElems = 0;
458
        dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
459
	dict->strings = NULL;
460
	dict->subdict = NULL;
461
        if (dict->dict) {
462
	    memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
463
	    return(dict);
464
        }
465
        xmlFree(dict);
466
    }
467
    return(NULL);
468
}
469
470
/**
471
 * xmlDictCreateSub:
472
 * @sub: an existing dictionnary
473
 *
474
 * Create a new dictionary, inheriting strings from the read-only
475
 * dictionnary @sub. On lookup, strings are first searched in the
476
 * new dictionnary, then in @sub, and if not found are created in the
477
 * new dictionnary.
478
 *
479
 * Returns the newly created dictionnary, or NULL if an error occured.
480
 */
481
xmlDictPtr
482
xmlDictCreateSub(xmlDictPtr sub) {
483
    xmlDictPtr dict = xmlDictCreate();
484
485
    if ((dict != NULL) && (sub != NULL)) {
486
#ifdef DICT_DEBUG_PATTERNS
487
        fprintf(stderr, "R");
488
#endif
489
        dict->subdict = sub;
490
	xmlDictReference(dict->subdict);
491
    }
492
    return(dict);
493
}
494
495
/**
496
 * xmlDictReference:
497
 * @dict: the dictionnary
498
 *
499
 * Increment the reference counter of a dictionary
500
 *
501
 * Returns 0 in case of success and -1 in case of error
502
 */
503
int
504
xmlDictReference(xmlDictPtr dict) {
505
    if (!xmlDictInitialized)
506
        if (!xmlInitializeDict())
507
            return(-1);
508
509
    if (dict == NULL) return -1;
510
    xmlRMutexLock(xmlDictMutex);
511
    dict->ref_counter++;
512
    xmlRMutexUnlock(xmlDictMutex);
513
    return(0);
514
}
515
516
/**
517
 * xmlDictGrow:
518
 * @dict: the dictionnary
519
 * @size: the new size of the dictionnary
520
 *
521
 * resize the dictionnary
522
 *
523
 * Returns 0 in case of success, -1 in case of failure
524
 */
525
static int
526
xmlDictGrow(xmlDictPtr dict, int size) {
527
    unsigned long key, okey;
528
    int oldsize, i;
529
    xmlDictEntryPtr iter, next;
530
    struct _xmlDictEntry *olddict;
531
#ifdef DEBUG_GROW
532
    unsigned long nbElem = 0;
533
#endif
534
    int ret = 0;
535
    int keep_keys = 1;
536
537
    if (dict == NULL)
538
	return(-1);
539
    if (size < 8)
540
        return(-1);
541
    if (size > 8 * 2048)
542
	return(-1);
543
544
#ifdef DICT_DEBUG_PATTERNS
545
    fprintf(stderr, "*");
546
#endif
547
548
    oldsize = dict->size;
549
    olddict = dict->dict;
550
    if (olddict == NULL)
551
        return(-1);
552
    if (oldsize == MIN_DICT_SIZE)
553
        keep_keys = 0;
554
555
    dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
556
    if (dict->dict == NULL) {
557
	dict->dict = olddict;
558
	return(-1);
559
    }
560
    memset(dict->dict, 0, size * sizeof(xmlDictEntry));
561
    dict->size = size;
562
563
    /*	If the two loops are merged, there would be situations where
564
	a new entry needs to allocated and data copied into it from
565
	the main dict. It is nicer to run through the array twice, first
566
	copying all the elements in the main array (less probability of
567
	allocate) and then the rest, so we only free in the second loop.
568
    */
569
    for (i = 0; i < oldsize; i++) {
570
	if (olddict[i].valid == 0)
571
	    continue;
572
573
	if (keep_keys)
574
	    okey = olddict[i].okey;
575
	else
576
	    okey = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
577
	key = okey % dict->size;
578
579
	if (dict->dict[key].valid == 0) {
580
	    memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
581
	    dict->dict[key].next = NULL;
582
	    dict->dict[key].okey = okey;
583
	} else {
584
	    xmlDictEntryPtr entry;
585
586
	    entry = xmlMalloc(sizeof(xmlDictEntry));
587
	    if (entry != NULL) {
588
		entry->name = olddict[i].name;
589
		entry->len = olddict[i].len;
590
		entry->okey = okey;
591
		entry->next = dict->dict[key].next;
592
		entry->valid = 1;
593
		dict->dict[key].next = entry;
594
	    } else {
595
	        /*
596
		 * we don't have much ways to alert from herei
597
		 * result is loosing an entry and unicity garantee
598
		 */
599
	        ret = -1;
600
	    }
601
	}
602
#ifdef DEBUG_GROW
603
	nbElem++;
604
#endif
605
    }
606
607
    for (i = 0; i < oldsize; i++) {
608
	iter = olddict[i].next;
609
	while (iter) {
610
	    next = iter->next;
611
612
	    /*
613
	     * put back the entry in the new dict
614
	     */
615
616
	    if (keep_keys)
617
		okey = iter->okey;
618
	    else
619
		okey = xmlDictComputeKey(dict, iter->name, iter->len);
620
	    key = okey % dict->size;
621
	    if (dict->dict[key].valid == 0) {
622
		memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
623
		dict->dict[key].next = NULL;
624
		dict->dict[key].valid = 1;
625
		dict->dict[key].okey = okey;
626
		xmlFree(iter);
627
	    } else {
628
		iter->next = dict->dict[key].next;
629
		iter->okey = okey;
630
		dict->dict[key].next = iter;
631
	    }
632
633
#ifdef DEBUG_GROW
634
	    nbElem++;
635
#endif
636
637
	    iter = next;
638
	}
639
    }
640
641
    xmlFree(olddict);
642
643
#ifdef DEBUG_GROW
644
    xmlGenericError(xmlGenericErrorContext,
645
	    "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
646
#endif
647
648
    return(ret);
649
}
650
651
/**
652
 * xmlDictFree:
653
 * @dict: the dictionnary
654
 *
655
 * Free the hash @dict and its contents. The userdata is
656
 * deallocated with @f if provided.
657
 */
658
void
659
xmlDictFree(xmlDictPtr dict) {
660
    int i;
661
    xmlDictEntryPtr iter;
662
    xmlDictEntryPtr next;
663
    int inside_dict = 0;
664
    xmlDictStringsPtr pool, nextp;
665
666
    if (dict == NULL)
667
	return;
668
669
    if (!xmlDictInitialized)
670
        if (!xmlInitializeDict())
671
            return;
672
673
    /* decrement the counter, it may be shared by a parser and docs */
674
    xmlRMutexLock(xmlDictMutex);
675
    dict->ref_counter--;
676
    if (dict->ref_counter > 0) {
677
        xmlRMutexUnlock(xmlDictMutex);
678
        return;
679
    }
680
681
    xmlRMutexUnlock(xmlDictMutex);
682
683
    if (dict->subdict != NULL) {
684
        xmlDictFree(dict->subdict);
685
    }
686
687
    if (dict->dict) {
688
	for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
689
	    iter = &(dict->dict[i]);
690
	    if (iter->valid == 0)
691
		continue;
692
	    inside_dict = 1;
693
	    while (iter) {
694
		next = iter->next;
695
		if (!inside_dict)
696
		    xmlFree(iter);
697
		dict->nbElems--;
698
		inside_dict = 0;
699
		iter = next;
700
	    }
701
	    inside_dict = 0;
702
	}
703
	xmlFree(dict->dict);
704
    }
705
    pool = dict->strings;
706
    while (pool != NULL) {
707
        nextp = pool->next;
708
	xmlFree(pool);
709
	pool = nextp;
710
    }
711
    xmlFree(dict);
712
}
713
714
/**
715
 * xmlDictLookup:
716
 * @dict: the dictionnary
717
 * @name: the name of the userdata
718
 * @len: the length of the name, if -1 it is recomputed
719
 *
720
 * Add the @name to the dictionnary @dict if not present.
721
 *
722
 * Returns the internal copy of the name or NULL in case of internal error
723
 */
724
const xmlChar *
725
xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
726
    unsigned long key, okey, nbi = 0;
727
    xmlDictEntryPtr entry;
728
    xmlDictEntryPtr insert;
729
    const xmlChar *ret;
730
731
    if ((dict == NULL) || (name == NULL))
732
	return(NULL);
733
734
    if (len < 0)
735
        len = strlen((const char *) name);
736
737
    /*
738
     * Check for duplicate and insertion location.
739
     */
740
    okey = xmlDictComputeKey(dict, name, len);
741
    key = okey % dict->size;
742
    if (dict->dict[key].valid == 0) {
743
	insert = NULL;
744
    } else {
745
	for (insert = &(dict->dict[key]); insert->next != NULL;
746
	     insert = insert->next) {
747
#ifdef __GNUC__
748
	    if ((insert->okey == okey) && (insert->len == len)) {
749
		if (!memcmp(insert->name, name, len))
750
		    return(insert->name);
751
	    }
752
#else
753
	    if ((insert->okey == okey) && (insert->len == len) &&
754
	        (!xmlStrncmp(insert->name, name, len)))
755
		return(insert->name);
756
#endif
757
	    nbi++;
758
	}
759
#ifdef __GNUC__
760
	if ((insert->okey == okey) && (insert->len == len)) {
761
	    if (!memcmp(insert->name, name, len))
762
		return(insert->name);
763
	}
764
#else
765
	if ((insert->okey == okey) && (insert->len == len) &&
766
	    (!xmlStrncmp(insert->name, name, len)))
767
	    return(insert->name);
768
#endif
769
    }
770
771
    if (dict->subdict) {
772
        unsigned long skey;
773
774
        /* we cannot always reuse the same okey for the subdict */
775
        if (((dict->size == MIN_DICT_SIZE) &&
776
	     (dict->subdict->size != MIN_DICT_SIZE)) ||
777
            ((dict->size != MIN_DICT_SIZE) &&
778
	     (dict->subdict->size == MIN_DICT_SIZE)))
779
	    skey = xmlDictComputeKey(dict->subdict, name, len);
780
	else
781
	    skey = okey;
782
783
	key = skey % dict->subdict->size;
784
	if (dict->subdict->dict[key].valid != 0) {
785
	    xmlDictEntryPtr tmp;
786
787
	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
788
		 tmp = tmp->next) {
789
#ifdef __GNUC__
790
		if ((tmp->okey == skey) && (tmp->len == len)) {
791
		    if (!memcmp(tmp->name, name, len))
792
			return(tmp->name);
793
		}
794
#else
795
		if ((tmp->okey == skey) && (tmp->len == len) &&
796
		    (!xmlStrncmp(tmp->name, name, len)))
797
		    return(tmp->name);
798
#endif
799
		nbi++;
800
	    }
801
#ifdef __GNUC__
802
	    if ((tmp->okey == skey) && (tmp->len == len)) {
803
		if (!memcmp(tmp->name, name, len))
804
		    return(tmp->name);
805
	    }
806
#else
807
	    if ((tmp->okey == skey) && (tmp->len == len) &&
808
		(!xmlStrncmp(tmp->name, name, len)))
809
		return(tmp->name);
810
#endif
811
	}
812
	key = okey % dict->size;
813
    }
814
815
    ret = xmlDictAddString(dict, name, len);
816
    if (ret == NULL)
817
        return(NULL);
818
    if (insert == NULL) {
819
	entry = &(dict->dict[key]);
820
    } else {
821
	entry = xmlMalloc(sizeof(xmlDictEntry));
822
	if (entry == NULL)
823
	     return(NULL);
824
    }
825
    entry->name = ret;
826
    entry->len = len;
827
    entry->next = NULL;
828
    entry->valid = 1;
829
    entry->okey = okey;
830
831
832
    if (insert != NULL) 
833
	insert->next = entry;
834
835
    dict->nbElems++;
836
837
    if ((nbi > MAX_HASH_LEN) &&
838
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) {
839
	if (xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size) != 0)
840
	    return(NULL);
841
    }
842
    /* Note that entry may have been freed at this point by xmlDictGrow */
843
844
    return(ret);
845
}
846
847
/**
848
 * xmlDictExists:
849
 * @dict: the dictionnary
850
 * @name: the name of the userdata
851
 * @len: the length of the name, if -1 it is recomputed
852
 *
853
 * Check if the @name exists in the dictionnary @dict.
854
 *
855
 * Returns the internal copy of the name or NULL if not found.
856
 */
857
const xmlChar *
858
xmlDictExists(xmlDictPtr dict, const xmlChar *name, int len) {
859
    unsigned long key, okey, nbi = 0;
860
    xmlDictEntryPtr insert;
861
862
    if ((dict == NULL) || (name == NULL))
863
	return(NULL);
864
865
    if (len < 0)
866
        len = strlen((const char *) name);
867
868
    /*
869
     * Check for duplicate and insertion location.
870
     */
871
    okey = xmlDictComputeKey(dict, name, len);
872
    key = okey % dict->size;
873
    if (dict->dict[key].valid == 0) {
874
	insert = NULL;
875
    } else {
876
	for (insert = &(dict->dict[key]); insert->next != NULL;
877
	     insert = insert->next) {
878
#ifdef __GNUC__
879
	    if ((insert->okey == okey) && (insert->len == len)) {
880
		if (!memcmp(insert->name, name, len))
881
		    return(insert->name);
882
	    }
883
#else
884
	    if ((insert->okey == okey) && (insert->len == len) &&
885
	        (!xmlStrncmp(insert->name, name, len)))
886
		return(insert->name);
887
#endif
888
	    nbi++;
889
	}
890
#ifdef __GNUC__
891
	if ((insert->okey == okey) && (insert->len == len)) {
892
	    if (!memcmp(insert->name, name, len))
893
		return(insert->name);
894
	}
895
#else
896
	if ((insert->okey == okey) && (insert->len == len) &&
897
	    (!xmlStrncmp(insert->name, name, len)))
898
	    return(insert->name);
899
#endif
900
    }
901
902
    if (dict->subdict) {
903
        unsigned long skey;
904
905
        /* we cannot always reuse the same okey for the subdict */
906
        if (((dict->size == MIN_DICT_SIZE) &&
907
	     (dict->subdict->size != MIN_DICT_SIZE)) ||
908
            ((dict->size != MIN_DICT_SIZE) &&
909
	     (dict->subdict->size == MIN_DICT_SIZE)))
910
	    skey = xmlDictComputeKey(dict->subdict, name, len);
911
	else
912
	    skey = okey;
913
914
	key = skey % dict->subdict->size;
915
	if (dict->subdict->dict[key].valid != 0) {
916
	    xmlDictEntryPtr tmp;
917
918
	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
919
		 tmp = tmp->next) {
920
#ifdef __GNUC__
921
		if ((tmp->okey == skey) && (tmp->len == len)) {
922
		    if (!memcmp(tmp->name, name, len))
923
			return(tmp->name);
924
		}
925
#else
926
		if ((tmp->okey == skey) && (tmp->len == len) &&
927
		    (!xmlStrncmp(tmp->name, name, len)))
928
		    return(tmp->name);
929
#endif
930
		nbi++;
931
	    }
932
#ifdef __GNUC__
933
	    if ((tmp->okey == skey) && (tmp->len == len)) {
934
		if (!memcmp(tmp->name, name, len))
935
		    return(tmp->name);
936
	    }
937
#else
938
	    if ((tmp->okey == skey) && (tmp->len == len) &&
939
		(!xmlStrncmp(tmp->name, name, len)))
940
		return(tmp->name);
941
#endif
942
	}
943
    }
944
945
    /* not found */
946
    return(NULL);
947
}
948
949
/**
950
 * xmlDictQLookup:
951
 * @dict: the dictionnary
952
 * @prefix: the prefix
953
 * @name: the name
954
 *
955
 * Add the QName @prefix:@name to the hash @dict if not present.
956
 *
957
 * Returns the internal copy of the QName or NULL in case of internal error
958
 */
959
const xmlChar *
960
xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
961
    unsigned long okey, key, nbi = 0;
962
    xmlDictEntryPtr entry;
963
    xmlDictEntryPtr insert;
964
    const xmlChar *ret;
965
    int len, plen, l;
966
967
    if ((dict == NULL) || (name == NULL))
968
	return(NULL);
969
    if (prefix == NULL)
970
        return(xmlDictLookup(dict, name, -1));
971
972
    l = len = strlen((const char *) name);
973
    plen = strlen((const char *) prefix);
974
    len += 1 + plen;
975
976
    /*
977
     * Check for duplicate and insertion location.
978
     */
979
    okey = xmlDictComputeQKey(dict, prefix, plen, name, l);
980
    key = okey % dict->size;
981
    if (dict->dict[key].valid == 0) {
982
	insert = NULL;
983
    } else {
984
	for (insert = &(dict->dict[key]); insert->next != NULL;
985
	     insert = insert->next) {
986
	    if ((insert->okey == okey) && (insert->len == len) &&
987
	        (xmlStrQEqual(prefix, name, insert->name)))
988
		return(insert->name);
989
	    nbi++;
990
	}
991
	if ((insert->okey == okey) && (insert->len == len) &&
992
	    (xmlStrQEqual(prefix, name, insert->name)))
993
	    return(insert->name);
994
    }
995
996
    if (dict->subdict) {
997
        unsigned long skey;
998
999
        /* we cannot always reuse the same okey for the subdict */
1000
        if (((dict->size == MIN_DICT_SIZE) &&
1001
	     (dict->subdict->size != MIN_DICT_SIZE)) ||
1002
            ((dict->size != MIN_DICT_SIZE) &&
1003
	     (dict->subdict->size == MIN_DICT_SIZE)))
1004
	    skey = xmlDictComputeQKey(dict->subdict, prefix, plen, name, l);
1005
	else
1006
	    skey = okey;
1007
1008
	key = skey % dict->subdict->size;
1009
	if (dict->subdict->dict[key].valid != 0) {
1010
	    xmlDictEntryPtr tmp;
1011
	    for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL;
1012
		 tmp = tmp->next) {
1013
		if ((tmp->okey == skey) && (tmp->len == len) &&
1014
		    (xmlStrQEqual(prefix, name, tmp->name)))
1015
		    return(tmp->name);
1016
		nbi++;
1017
	    }
1018
	    if ((tmp->okey == skey) && (tmp->len == len) &&
1019
		(xmlStrQEqual(prefix, name, tmp->name)))
1020
		return(tmp->name);
1021
	}
1022
	key = okey % dict->size;
1023
    }
1024
1025
    ret = xmlDictAddQString(dict, prefix, plen, name, l);
1026
    if (ret == NULL)
1027
        return(NULL);
1028
    if (insert == NULL) {
1029
	entry = &(dict->dict[key]);
1030
    } else {
1031
	entry = xmlMalloc(sizeof(xmlDictEntry));
1032
	if (entry == NULL)
1033
	     return(NULL);
1034
    }
1035
    entry->name = ret;
1036
    entry->len = len;
1037
    entry->next = NULL;
1038
    entry->valid = 1;
1039
    entry->okey = okey;
1040
1041
    if (insert != NULL) 
1042
	insert->next = entry;
1043
1044
    dict->nbElems++;
1045
1046
    if ((nbi > MAX_HASH_LEN) &&
1047
        (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
1048
	xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
1049
    /* Note that entry may have been freed at this point by xmlDictGrow */
1050
1051
    return(ret);
1052
}
1053
1054
/**
1055
 * xmlDictOwns:
1056
 * @dict: the dictionnary
1057
 * @str: the string
1058
 *
1059
 * check if a string is owned by the disctionary
1060
 *
1061
 * Returns 1 if true, 0 if false and -1 in case of error
1062
 * -1 in case of error
1063
 */
1064
int
1065
xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
1066
    xmlDictStringsPtr pool;
1067
1068
    if ((dict == NULL) || (str == NULL))
1069
	return(-1);
1070
    pool = dict->strings;
1071
    while (pool != NULL) {
1072
        if ((str >= &pool->array[0]) && (str <= pool->free))
1073
	    return(1);
1074
	pool = pool->next;
1075
    }
1076
    if (dict->subdict)
1077
        return(xmlDictOwns(dict->subdict, str));
1078
    return(0);
1079
}
1080
1081
/**
1082
 * xmlDictSize:
1083
 * @dict: the dictionnary
1084
 *
1085
 * Query the number of elements installed in the hash @dict.
1086
 *
1087
 * Returns the number of elements in the dictionnary or
1088
 * -1 in case of error
1089
 */
1090
int
1091
xmlDictSize(xmlDictPtr dict) {
1092
    if (dict == NULL)
1093
	return(-1);
1094
    if (dict->subdict)
1095
        return(dict->nbElems + dict->subdict->nbElems);
1096
    return(dict->nbElems);
1097
}
1098
1099
1100
#define bottom_dict
1101
#include "elfgcchack.h"