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