blob: b56cef28082243f31c83f75e3b116bfbd38c1778 [file] [log] [blame]
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001/*
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#include <libxml/tree.h>
24#include <libxml/dict.h>
25#include <libxml/xmlmemory.h>
26#include <libxml/xmlerror.h>
27#include <libxml/globals.h>
28
29#define MAX_HASH_LEN 4
30#define MIN_DICT_SIZE 128
31#define MAX_DICT_HASH 8 * 2048
32
33/* #define ALLOW_REMOVAL */
34/* #define DEBUG_GROW */
35
36/*
37 * An entry in the dictionnary
38 */
39typedef struct _xmlDictEntry xmlDictEntry;
40typedef xmlDictEntry *xmlDictEntryPtr;
41struct _xmlDictEntry {
42 struct _xmlDictEntry *next;
Daniel Veillard81514ba2003-09-16 23:17:26 +000043 const xmlChar *name;
Daniel Veillard2fdbd322003-08-18 12:15:38 +000044 int len;
45 int valid;
46};
47
Daniel Veillard81514ba2003-09-16 23:17:26 +000048typedef struct _xmlDictStrings xmlDictStrings;
49typedef xmlDictStrings *xmlDictStringsPtr;
50struct _xmlDictStrings {
51 xmlDictStringsPtr next;
52 xmlChar *free;
53 xmlChar *end;
54 int size;
55 int nbStrings;
56 xmlChar array[1];
57};
Daniel Veillard2fdbd322003-08-18 12:15:38 +000058/*
59 * The entire dictionnary
60 */
61struct _xmlDict {
Daniel Veillarde96a2a42003-09-24 21:23:56 +000062 int ref_counter;
63
Daniel Veillard2fdbd322003-08-18 12:15:38 +000064 struct _xmlDictEntry *dict;
65 int size;
66 int nbElems;
Daniel Veillard81514ba2003-09-16 23:17:26 +000067 xmlDictStringsPtr strings;
Daniel Veillard2fdbd322003-08-18 12:15:38 +000068};
69
70/*
Daniel Veillard81514ba2003-09-16 23:17:26 +000071 * xmlDictAddString:
72 * @dict: the dictionnary
73 * @name: the name of the userdata
74 * @len: the length of the name, if -1 it is recomputed
75 *
76 * Add the string to the array[s]
77 *
78 * Returns the pointer of the local string, or NULL in case of error.
79 */
80static const xmlChar *
81xmlDictAddString(xmlDictPtr dict, const xmlChar *name, int namelen) {
82 xmlDictStringsPtr pool;
83 const xmlChar *ret;
84 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
85
86 pool = dict->strings;
87 while (pool != NULL) {
88 if (pool->end - pool->free > namelen)
89 goto found_pool;
90 if (pool->size > size) size = pool->size;
91 pool = pool->next;
92 }
93 /*
94 * Not found, need to allocate
95 */
96 if (pool == NULL) {
97 if (size == 0) size = 1000;
98 else size *= 4; /* exponential growth */
99 if (size < 4 * namelen)
100 size = 4 * namelen; /* just in case ! */
101 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
102 if (pool == NULL)
103 return(NULL);
104 pool->size = size;
105 pool->nbStrings = 0;
106 pool->free = &pool->array[0];
107 pool->end = &pool->array[size];
108 pool->next = dict->strings;
109 dict->strings = pool;
110 }
111found_pool:
112 ret = pool->free;
113 memcpy(pool->free, name, namelen);
114 pool->free += namelen;
115 *(pool->free++) = 0;
116 return(ret);
117}
118
119/*
Daniel Veillarde72c5082003-09-19 12:44:05 +0000120 * xmlDictAddQString:
121 * @dict: the dictionnary
122 * @prefix: the prefix of the userdata
123 * @name: the name of the userdata
124 * @len: the length of the name, if -1 it is recomputed
125 *
126 * Add the QName to the array[s]
127 *
128 * Returns the pointer of the local string, or NULL in case of error.
129 */
130static const xmlChar *
131xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix,
132 const xmlChar *name, int namelen)
133{
134 xmlDictStringsPtr pool;
135 const xmlChar *ret;
136 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
137 int plen;
138
139 if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
140 plen = xmlStrlen(prefix);
141
142 pool = dict->strings;
143 while (pool != NULL) {
144 if (pool->end - pool->free > namelen)
145 goto found_pool;
146 if (pool->size > size) size = pool->size;
147 pool = pool->next;
148 }
149 /*
150 * Not found, need to allocate
151 */
152 if (pool == NULL) {
153 if (size == 0) size = 1000;
154 else size *= 4; /* exponential growth */
155 if (size < 4 * namelen)
156 size = 4 * namelen; /* just in case ! */
157 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
158 if (pool == NULL)
159 return(NULL);
160 pool->size = size;
161 pool->nbStrings = 0;
162 pool->free = &pool->array[0];
163 pool->end = &pool->array[size];
164 pool->next = dict->strings;
165 dict->strings = pool;
166 }
167found_pool:
168 ret = pool->free;
169 memcpy(pool->free, prefix, plen);
170 pool->free += plen;
171 *(pool->free++) = ':';
172 namelen -= plen + 1;
173 memcpy(pool->free, name, namelen);
174 pool->free += namelen;
175 *(pool->free++) = 0;
176 return(ret);
177}
178
179/*
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000180 * xmlDictComputeKey:
181 * Calculate the hash key
182 */
183static unsigned long
184xmlDictComputeKey(xmlDictPtr dict, const xmlChar *name, int namelen) {
185 unsigned long value = 0L;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000186
187 if (name == NULL) return(0);
188 value += 30 * (*name);
189 if (namelen > 10) {
190 value += name[namelen - 1];
191 namelen = 10;
192 }
193 switch (namelen) {
194 case 10: value += name[9];
195 case 9: value += name[8];
196 case 8: value += name[7];
197 case 7: value += name[6];
198 case 6: value += name[5];
199 case 5: value += name[4];
200 case 4: value += name[3];
201 case 3: value += name[2];
202 case 2: value += name[1];
203 case 1: value += name[0];
204 default: break;
205 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000206 return (value % dict->size);
207}
208
209/*
210 * xmlDictComputeQKey:
211 * Calculate the hash key
212 */
213static unsigned long
214xmlDictComputeQKey(xmlDictPtr dict, const xmlChar *prefix,
215 const xmlChar *name, int len)
216{
217 unsigned long value = 0L;
218 int plen;
219
220 if (prefix == NULL)
221 return(xmlDictComputeKey(dict, name, len));
222
223 plen = xmlStrlen(prefix);
224 if (plen == 0)
225 value += 30 * (unsigned long) ':';
226 else
227 value += 30 * (*prefix);
228
229 if (len > 10) {
230 value += name[len - (plen + 1 + 1)];
231 len = 10;
232 if (plen > 10)
233 plen = 10;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000234 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000235 switch (plen) {
236 case 10: value += prefix[9];
237 case 9: value += prefix[8];
238 case 8: value += prefix[7];
239 case 7: value += prefix[6];
240 case 6: value += prefix[5];
241 case 5: value += prefix[4];
242 case 4: value += prefix[3];
243 case 3: value += prefix[2];
244 case 2: value += prefix[1];
245 case 1: value += prefix[0];
246 default: break;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000247 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000248 len -= plen;
249 if (len > 0) {
250 value += (unsigned long) ':';
251 len--;
252 }
253 switch (len) {
254 case 10: value += name[9];
255 case 9: value += name[8];
256 case 8: value += name[7];
257 case 7: value += name[6];
258 case 6: value += name[5];
259 case 5: value += name[4];
260 case 4: value += name[3];
261 case 3: value += name[2];
262 case 2: value += name[1];
263 case 1: value += name[0];
264 default: break;
265 }
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000266 return (value % dict->size);
267}
268
269/**
270 * xmlDictCreate:
271 *
272 * Create a new dictionary
273 *
274 * Returns the newly created object, or NULL if an error occured.
275 */
276xmlDictPtr
277xmlDictCreate(void) {
278 xmlDictPtr dict;
279
280 dict = xmlMalloc(sizeof(xmlDict));
281 if (dict) {
Daniel Veillarde96a2a42003-09-24 21:23:56 +0000282 dict->ref_counter = 1;
283
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000284 dict->size = MIN_DICT_SIZE;
285 dict->nbElems = 0;
286 dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
Daniel Veillard81514ba2003-09-16 23:17:26 +0000287 dict->strings = NULL;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000288 if (dict->dict) {
289 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
290 return(dict);
291 }
292 xmlFree(dict);
293 }
294 return(NULL);
295}
296
297/**
Daniel Veillarde96a2a42003-09-24 21:23:56 +0000298 * xmlDictReference:
299 * @dict: the dictionnary
300 *
301 * Increment the reference counter of a dictionary
302 *
303 * Returns 0 in case of success and -1 in case of error
304 */
305int
306xmlDictReference(xmlDictPtr dict) {
307 if (dict == NULL) return -1;
308 dict->ref_counter++;
309 return(0);
310}
311
312/**
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000313 * xmlDictGrow:
314 * @dict: the dictionnary
315 * @size: the new size of the dictionnary
316 *
317 * resize the dictionnary
318 *
319 * Returns 0 in case of success, -1 in case of failure
320 */
321static int
322xmlDictGrow(xmlDictPtr dict, int size) {
323 unsigned long key;
324 int oldsize, i;
325 xmlDictEntryPtr iter, next;
326 struct _xmlDictEntry *olddict;
327#ifdef DEBUG_GROW
328 unsigned long nbElem = 0;
329#endif
330
331 if (dict == NULL)
332 return(-1);
333 if (size < 8)
334 return(-1);
335 if (size > 8 * 2048)
336 return(-1);
337
338 oldsize = dict->size;
339 olddict = dict->dict;
340 if (olddict == NULL)
341 return(-1);
342
343 dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
344 if (dict->dict == NULL) {
345 dict->dict = olddict;
346 return(-1);
347 }
348 memset(dict->dict, 0, size * sizeof(xmlDictEntry));
349 dict->size = size;
350
351 /* If the two loops are merged, there would be situations where
352 a new entry needs to allocated and data copied into it from
353 the main dict. So instead, we run through the array twice, first
354 copying all the elements in the main array (where we can't get
355 conflicts) and then the rest, so we only free (and don't allocate)
356 */
357 for (i = 0; i < oldsize; i++) {
358 if (olddict[i].valid == 0)
359 continue;
360 key = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
361 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
362 dict->dict[key].next = NULL;
363#ifdef DEBUG_GROW
364 nbElem++;
365#endif
366 }
367
368 for (i = 0; i < oldsize; i++) {
369 iter = olddict[i].next;
370 while (iter) {
371 next = iter->next;
372
373 /*
374 * put back the entry in the new dict
375 */
376
377 key = xmlDictComputeKey(dict, iter->name, iter->len);
378 if (dict->dict[key].valid == 0) {
379 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
380 dict->dict[key].next = NULL;
381 dict->dict[key].valid = 1;
382 xmlFree(iter);
383 } else {
384 iter->next = dict->dict[key].next;
385 dict->dict[key].next = iter;
386 }
387
388#ifdef DEBUG_GROW
389 nbElem++;
390#endif
391
392 iter = next;
393 }
394 }
395
396 xmlFree(olddict);
397
398#ifdef DEBUG_GROW
399 xmlGenericError(xmlGenericErrorContext,
400 "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
401#endif
402
403 return(0);
404}
405
406/**
407 * xmlDictFree:
408 * @dict: the dictionnary
409 *
410 * Free the hash @dict and its contents. The userdata is
411 * deallocated with @f if provided.
412 */
413void
414xmlDictFree(xmlDictPtr dict) {
415 int i;
416 xmlDictEntryPtr iter;
417 xmlDictEntryPtr next;
418 int inside_dict = 0;
Daniel Veillard81514ba2003-09-16 23:17:26 +0000419 xmlDictStringsPtr pool, nextp;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000420
421 if (dict == NULL)
422 return;
Daniel Veillarde96a2a42003-09-24 21:23:56 +0000423
424 /* decrement the counter, it may be shared by a parser and docs */
425 dict->ref_counter--;
426 if (dict->ref_counter > 0) return;
427
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000428 if (dict->dict) {
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000429 for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000430 iter = &(dict->dict[i]);
431 if (iter->valid == 0)
432 continue;
433 inside_dict = 1;
434 while (iter) {
435 next = iter->next;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000436 if (!inside_dict)
437 xmlFree(iter);
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000438 dict->nbElems--;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000439 inside_dict = 0;
440 iter = next;
441 }
442 inside_dict = 0;
443 }
444 xmlFree(dict->dict);
445 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000446 pool = dict->strings;
447 while (pool != NULL) {
448 nextp = pool->next;
449 xmlFree(pool);
450 pool = nextp;
451 }
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000452 xmlFree(dict);
453}
454
455/**
456 * xmlDictLookup:
457 * @dict: the dictionnary
458 * @name: the name of the userdata
Daniel Veillard0fb18932003-09-07 09:14:37 +0000459 * @len: the length of the name, if -1 it is recomputed
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000460 *
461 * Add the @name to the hash @dict if not present.
462 *
463 * Returns the internal copy of the name or NULL in case of internal error
464 */
465const xmlChar *
466xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
467 unsigned long key, nbi = 0;
468 xmlDictEntryPtr entry;
469 xmlDictEntryPtr insert;
470 const xmlChar *ret;
471
Daniel Veillard0fb18932003-09-07 09:14:37 +0000472 if ((dict == NULL) || (name == NULL))
473 return(NULL);
474
475 if (len < 0)
476 len = xmlStrlen(name);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000477
478 /*
479 * Check for duplicate and insertion location.
480 */
481 key = xmlDictComputeKey(dict, name, len);
482 if (dict->dict[key].valid == 0) {
483 insert = NULL;
484 } else {
485 for (insert = &(dict->dict[key]); insert->next != NULL;
486 insert = insert->next) {
487 if ((insert->len == len) &&
488 (!xmlStrncmp(insert->name, name, len)))
489 return(insert->name);
490 nbi++;
491 }
492 if ((insert->len == len) &&
493 (!xmlStrncmp(insert->name, name, len)))
494 return(insert->name);
495 }
496
Daniel Veillard81514ba2003-09-16 23:17:26 +0000497 ret = xmlDictAddString(dict, name, len);
498 if (ret == NULL)
499 return(NULL);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000500 if (insert == NULL) {
501 entry = &(dict->dict[key]);
502 } else {
503 entry = xmlMalloc(sizeof(xmlDictEntry));
504 if (entry == NULL)
505 return(NULL);
506 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000507 entry->name = ret;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000508 entry->len = len;
509 entry->next = NULL;
510 entry->valid = 1;
511
512
513 if (insert != NULL)
514 insert->next = entry;
515
516 dict->nbElems++;
517
518 if ((nbi > MAX_HASH_LEN) &&
519 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
520 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
521 /* Note that entry may have been freed at this point by xmlDictGrow */
522
523 return(ret);
524}
525
526/**
Daniel Veillarde72c5082003-09-19 12:44:05 +0000527 * xmlDictQLookup:
528 * @dict: the dictionnary
529 * @prefix: the prefix
530 * @name: the name
531 *
532 * Add the QName @prefix:@name to the hash @dict if not present.
533 *
534 * Returns the internal copy of the QName or NULL in case of internal error
535 */
536const xmlChar *
537xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
538 unsigned long key, nbi = 0;
539 xmlDictEntryPtr entry;
540 xmlDictEntryPtr insert;
541 const xmlChar *ret;
542 int len;
543
544 if ((dict == NULL) || (name == NULL))
545 return(NULL);
546
547 len = xmlStrlen(name);
548 if (prefix != NULL)
549 len += 1 + xmlStrlen(prefix);
550
551 /*
552 * Check for duplicate and insertion location.
553 */
554 key = xmlDictComputeQKey(dict, prefix, name, len);
555 if (dict->dict[key].valid == 0) {
556 insert = NULL;
557 } else {
558 for (insert = &(dict->dict[key]); insert->next != NULL;
559 insert = insert->next) {
560 if ((insert->len == len) &&
561 (xmlStrQEqual(prefix, name, insert->name)))
562 return(insert->name);
563 nbi++;
564 }
565 if ((insert->len == len) &&
566 (xmlStrQEqual(prefix, name, insert->name)))
567 return(insert->name);
568 }
569
570 ret = xmlDictAddQString(dict, prefix, name, len);
571 if (ret == NULL)
572 return(NULL);
573 if (insert == NULL) {
574 entry = &(dict->dict[key]);
575 } else {
576 entry = xmlMalloc(sizeof(xmlDictEntry));
577 if (entry == NULL)
578 return(NULL);
579 }
580 entry->name = ret;
581 entry->len = len;
582 entry->next = NULL;
583 entry->valid = 1;
584
585 if (insert != NULL)
586 insert->next = entry;
587
588 dict->nbElems++;
589
590 if ((nbi > MAX_HASH_LEN) &&
591 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
592 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
593 /* Note that entry may have been freed at this point by xmlDictGrow */
594
595 return(ret);
596}
597
598/**
Daniel Veillard81514ba2003-09-16 23:17:26 +0000599 * xmlDictOwns:
600 * @dict: the dictionnary
601 * @str: the string
602 *
603 * check if a string is owned by the disctionary
604 *
605 * Returns 1 if true, 0 if false and -1 in case of error
606 * -1 in case of error
607 */
608int
609xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
610 xmlDictStringsPtr pool;
611
612 if ((dict == NULL) || (str == NULL))
613 return(-1);
614 pool = dict->strings;
615 while (pool != NULL) {
616 if ((str >= pool->array) && (str <= pool->free))
617 return(1);
618 pool = pool->next;
619 }
620 return(0);
621}
Daniel Veillarde96a2a42003-09-24 21:23:56 +0000622
Daniel Veillard81514ba2003-09-16 23:17:26 +0000623/**
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000624 * xmlDictSize:
625 * @dict: the dictionnary
626 *
627 * Query the number of elements installed in the hash @dict.
628 *
629 * Returns the number of elements in the dictionnary or
630 * -1 in case of error
631 */
632int
633xmlDictSize(xmlDictPtr dict) {
634 if (dict == NULL)
635 return(-1);
636 return(dict->nbElems);
637}
638
639