blob: 93c8b8ed129d825d05975de8b64bbd0686b914d3 [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 {
62 struct _xmlDictEntry *dict;
63 int size;
64 int nbElems;
Daniel Veillard81514ba2003-09-16 23:17:26 +000065 xmlDictStringsPtr strings;
Daniel Veillard2fdbd322003-08-18 12:15:38 +000066};
67
68/*
Daniel Veillard81514ba2003-09-16 23:17:26 +000069 * xmlDictAddString:
70 * @dict: the dictionnary
71 * @name: the name of the userdata
72 * @len: the length of the name, if -1 it is recomputed
73 *
74 * Add the string to the array[s]
75 *
76 * Returns the pointer of the local string, or NULL in case of error.
77 */
78static const xmlChar *
79xmlDictAddString(xmlDictPtr dict, const xmlChar *name, int namelen) {
80 xmlDictStringsPtr pool;
81 const xmlChar *ret;
82 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
83
84 pool = dict->strings;
85 while (pool != NULL) {
86 if (pool->end - pool->free > namelen)
87 goto found_pool;
88 if (pool->size > size) size = pool->size;
89 pool = pool->next;
90 }
91 /*
92 * Not found, need to allocate
93 */
94 if (pool == NULL) {
95 if (size == 0) size = 1000;
96 else size *= 4; /* exponential growth */
97 if (size < 4 * namelen)
98 size = 4 * namelen; /* just in case ! */
99 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
100 if (pool == NULL)
101 return(NULL);
102 pool->size = size;
103 pool->nbStrings = 0;
104 pool->free = &pool->array[0];
105 pool->end = &pool->array[size];
106 pool->next = dict->strings;
107 dict->strings = pool;
108 }
109found_pool:
110 ret = pool->free;
111 memcpy(pool->free, name, namelen);
112 pool->free += namelen;
113 *(pool->free++) = 0;
114 return(ret);
115}
116
117/*
Daniel Veillarde72c5082003-09-19 12:44:05 +0000118 * xmlDictAddQString:
119 * @dict: the dictionnary
120 * @prefix: the prefix of the userdata
121 * @name: the name of the userdata
122 * @len: the length of the name, if -1 it is recomputed
123 *
124 * Add the QName to the array[s]
125 *
126 * Returns the pointer of the local string, or NULL in case of error.
127 */
128static const xmlChar *
129xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix,
130 const xmlChar *name, int namelen)
131{
132 xmlDictStringsPtr pool;
133 const xmlChar *ret;
134 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */
135 int plen;
136
137 if (prefix == NULL) return(xmlDictAddString(dict, name, namelen));
138 plen = xmlStrlen(prefix);
139
140 pool = dict->strings;
141 while (pool != NULL) {
142 if (pool->end - pool->free > namelen)
143 goto found_pool;
144 if (pool->size > size) size = pool->size;
145 pool = pool->next;
146 }
147 /*
148 * Not found, need to allocate
149 */
150 if (pool == NULL) {
151 if (size == 0) size = 1000;
152 else size *= 4; /* exponential growth */
153 if (size < 4 * namelen)
154 size = 4 * namelen; /* just in case ! */
155 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size);
156 if (pool == NULL)
157 return(NULL);
158 pool->size = size;
159 pool->nbStrings = 0;
160 pool->free = &pool->array[0];
161 pool->end = &pool->array[size];
162 pool->next = dict->strings;
163 dict->strings = pool;
164 }
165found_pool:
166 ret = pool->free;
167 memcpy(pool->free, prefix, plen);
168 pool->free += plen;
169 *(pool->free++) = ':';
170 namelen -= plen + 1;
171 memcpy(pool->free, name, namelen);
172 pool->free += namelen;
173 *(pool->free++) = 0;
174 return(ret);
175}
176
177/*
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000178 * xmlDictComputeKey:
179 * Calculate the hash key
180 */
181static unsigned long
182xmlDictComputeKey(xmlDictPtr dict, const xmlChar *name, int namelen) {
183 unsigned long value = 0L;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000184
185 if (name == NULL) return(0);
186 value += 30 * (*name);
187 if (namelen > 10) {
188 value += name[namelen - 1];
189 namelen = 10;
190 }
191 switch (namelen) {
192 case 10: value += name[9];
193 case 9: value += name[8];
194 case 8: value += name[7];
195 case 7: value += name[6];
196 case 6: value += name[5];
197 case 5: value += name[4];
198 case 4: value += name[3];
199 case 3: value += name[2];
200 case 2: value += name[1];
201 case 1: value += name[0];
202 default: break;
203 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000204 return (value % dict->size);
205}
206
207/*
208 * xmlDictComputeQKey:
209 * Calculate the hash key
210 */
211static unsigned long
212xmlDictComputeQKey(xmlDictPtr dict, const xmlChar *prefix,
213 const xmlChar *name, int len)
214{
215 unsigned long value = 0L;
216 int plen;
217
218 if (prefix == NULL)
219 return(xmlDictComputeKey(dict, name, len));
220
221 plen = xmlStrlen(prefix);
222 if (plen == 0)
223 value += 30 * (unsigned long) ':';
224 else
225 value += 30 * (*prefix);
226
227 if (len > 10) {
228 value += name[len - (plen + 1 + 1)];
229 len = 10;
230 if (plen > 10)
231 plen = 10;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000232 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000233 switch (plen) {
234 case 10: value += prefix[9];
235 case 9: value += prefix[8];
236 case 8: value += prefix[7];
237 case 7: value += prefix[6];
238 case 6: value += prefix[5];
239 case 5: value += prefix[4];
240 case 4: value += prefix[3];
241 case 3: value += prefix[2];
242 case 2: value += prefix[1];
243 case 1: value += prefix[0];
244 default: break;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000245 }
Daniel Veillarde72c5082003-09-19 12:44:05 +0000246 len -= plen;
247 if (len > 0) {
248 value += (unsigned long) ':';
249 len--;
250 }
251 switch (len) {
252 case 10: value += name[9];
253 case 9: value += name[8];
254 case 8: value += name[7];
255 case 7: value += name[6];
256 case 6: value += name[5];
257 case 5: value += name[4];
258 case 4: value += name[3];
259 case 3: value += name[2];
260 case 2: value += name[1];
261 case 1: value += name[0];
262 default: break;
263 }
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000264 return (value % dict->size);
265}
266
267/**
268 * xmlDictCreate:
269 *
270 * Create a new dictionary
271 *
272 * Returns the newly created object, or NULL if an error occured.
273 */
274xmlDictPtr
275xmlDictCreate(void) {
276 xmlDictPtr dict;
277
278 dict = xmlMalloc(sizeof(xmlDict));
279 if (dict) {
280 dict->size = MIN_DICT_SIZE;
281 dict->nbElems = 0;
282 dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
Daniel Veillard81514ba2003-09-16 23:17:26 +0000283 dict->strings = NULL;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000284 if (dict->dict) {
285 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
286 return(dict);
287 }
288 xmlFree(dict);
289 }
290 return(NULL);
291}
292
293/**
294 * xmlDictGrow:
295 * @dict: the dictionnary
296 * @size: the new size of the dictionnary
297 *
298 * resize the dictionnary
299 *
300 * Returns 0 in case of success, -1 in case of failure
301 */
302static int
303xmlDictGrow(xmlDictPtr dict, int size) {
304 unsigned long key;
305 int oldsize, i;
306 xmlDictEntryPtr iter, next;
307 struct _xmlDictEntry *olddict;
308#ifdef DEBUG_GROW
309 unsigned long nbElem = 0;
310#endif
311
312 if (dict == NULL)
313 return(-1);
314 if (size < 8)
315 return(-1);
316 if (size > 8 * 2048)
317 return(-1);
318
319 oldsize = dict->size;
320 olddict = dict->dict;
321 if (olddict == NULL)
322 return(-1);
323
324 dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
325 if (dict->dict == NULL) {
326 dict->dict = olddict;
327 return(-1);
328 }
329 memset(dict->dict, 0, size * sizeof(xmlDictEntry));
330 dict->size = size;
331
332 /* If the two loops are merged, there would be situations where
333 a new entry needs to allocated and data copied into it from
334 the main dict. So instead, we run through the array twice, first
335 copying all the elements in the main array (where we can't get
336 conflicts) and then the rest, so we only free (and don't allocate)
337 */
338 for (i = 0; i < oldsize; i++) {
339 if (olddict[i].valid == 0)
340 continue;
341 key = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
342 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
343 dict->dict[key].next = NULL;
344#ifdef DEBUG_GROW
345 nbElem++;
346#endif
347 }
348
349 for (i = 0; i < oldsize; i++) {
350 iter = olddict[i].next;
351 while (iter) {
352 next = iter->next;
353
354 /*
355 * put back the entry in the new dict
356 */
357
358 key = xmlDictComputeKey(dict, iter->name, iter->len);
359 if (dict->dict[key].valid == 0) {
360 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
361 dict->dict[key].next = NULL;
362 dict->dict[key].valid = 1;
363 xmlFree(iter);
364 } else {
365 iter->next = dict->dict[key].next;
366 dict->dict[key].next = iter;
367 }
368
369#ifdef DEBUG_GROW
370 nbElem++;
371#endif
372
373 iter = next;
374 }
375 }
376
377 xmlFree(olddict);
378
379#ifdef DEBUG_GROW
380 xmlGenericError(xmlGenericErrorContext,
381 "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
382#endif
383
384 return(0);
385}
386
387/**
388 * xmlDictFree:
389 * @dict: the dictionnary
390 *
391 * Free the hash @dict and its contents. The userdata is
392 * deallocated with @f if provided.
393 */
394void
395xmlDictFree(xmlDictPtr dict) {
396 int i;
397 xmlDictEntryPtr iter;
398 xmlDictEntryPtr next;
399 int inside_dict = 0;
Daniel Veillard81514ba2003-09-16 23:17:26 +0000400 xmlDictStringsPtr pool, nextp;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000401
402 if (dict == NULL)
403 return;
404 if (dict->dict) {
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000405 for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000406 iter = &(dict->dict[i]);
407 if (iter->valid == 0)
408 continue;
409 inside_dict = 1;
410 while (iter) {
411 next = iter->next;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000412 if (!inside_dict)
413 xmlFree(iter);
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000414 dict->nbElems--;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000415 inside_dict = 0;
416 iter = next;
417 }
418 inside_dict = 0;
419 }
420 xmlFree(dict->dict);
421 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000422 pool = dict->strings;
423 while (pool != NULL) {
424 nextp = pool->next;
425 xmlFree(pool);
426 pool = nextp;
427 }
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000428 xmlFree(dict);
429}
430
431/**
432 * xmlDictLookup:
433 * @dict: the dictionnary
434 * @name: the name of the userdata
Daniel Veillard0fb18932003-09-07 09:14:37 +0000435 * @len: the length of the name, if -1 it is recomputed
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000436 *
437 * Add the @name to the hash @dict if not present.
438 *
439 * Returns the internal copy of the name or NULL in case of internal error
440 */
441const xmlChar *
442xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
443 unsigned long key, nbi = 0;
444 xmlDictEntryPtr entry;
445 xmlDictEntryPtr insert;
446 const xmlChar *ret;
447
Daniel Veillard0fb18932003-09-07 09:14:37 +0000448 if ((dict == NULL) || (name == NULL))
449 return(NULL);
450
451 if (len < 0)
452 len = xmlStrlen(name);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000453
454 /*
455 * Check for duplicate and insertion location.
456 */
457 key = xmlDictComputeKey(dict, name, len);
458 if (dict->dict[key].valid == 0) {
459 insert = NULL;
460 } else {
461 for (insert = &(dict->dict[key]); insert->next != NULL;
462 insert = insert->next) {
463 if ((insert->len == len) &&
464 (!xmlStrncmp(insert->name, name, len)))
465 return(insert->name);
466 nbi++;
467 }
468 if ((insert->len == len) &&
469 (!xmlStrncmp(insert->name, name, len)))
470 return(insert->name);
471 }
472
Daniel Veillard81514ba2003-09-16 23:17:26 +0000473 ret = xmlDictAddString(dict, name, len);
474 if (ret == NULL)
475 return(NULL);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000476 if (insert == NULL) {
477 entry = &(dict->dict[key]);
478 } else {
479 entry = xmlMalloc(sizeof(xmlDictEntry));
480 if (entry == NULL)
481 return(NULL);
482 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000483 entry->name = ret;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000484 entry->len = len;
485 entry->next = NULL;
486 entry->valid = 1;
487
488
489 if (insert != NULL)
490 insert->next = entry;
491
492 dict->nbElems++;
493
494 if ((nbi > MAX_HASH_LEN) &&
495 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
496 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
497 /* Note that entry may have been freed at this point by xmlDictGrow */
498
499 return(ret);
500}
501
502/**
Daniel Veillarde72c5082003-09-19 12:44:05 +0000503 * xmlDictQLookup:
504 * @dict: the dictionnary
505 * @prefix: the prefix
506 * @name: the name
507 *
508 * Add the QName @prefix:@name to the hash @dict if not present.
509 *
510 * Returns the internal copy of the QName or NULL in case of internal error
511 */
512const xmlChar *
513xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) {
514 unsigned long key, nbi = 0;
515 xmlDictEntryPtr entry;
516 xmlDictEntryPtr insert;
517 const xmlChar *ret;
518 int len;
519
520 if ((dict == NULL) || (name == NULL))
521 return(NULL);
522
523 len = xmlStrlen(name);
524 if (prefix != NULL)
525 len += 1 + xmlStrlen(prefix);
526
527 /*
528 * Check for duplicate and insertion location.
529 */
530 key = xmlDictComputeQKey(dict, prefix, name, len);
531 if (dict->dict[key].valid == 0) {
532 insert = NULL;
533 } else {
534 for (insert = &(dict->dict[key]); insert->next != NULL;
535 insert = insert->next) {
536 if ((insert->len == len) &&
537 (xmlStrQEqual(prefix, name, insert->name)))
538 return(insert->name);
539 nbi++;
540 }
541 if ((insert->len == len) &&
542 (xmlStrQEqual(prefix, name, insert->name)))
543 return(insert->name);
544 }
545
546 ret = xmlDictAddQString(dict, prefix, name, len);
547 if (ret == NULL)
548 return(NULL);
549 if (insert == NULL) {
550 entry = &(dict->dict[key]);
551 } else {
552 entry = xmlMalloc(sizeof(xmlDictEntry));
553 if (entry == NULL)
554 return(NULL);
555 }
556 entry->name = ret;
557 entry->len = len;
558 entry->next = NULL;
559 entry->valid = 1;
560
561 if (insert != NULL)
562 insert->next = entry;
563
564 dict->nbElems++;
565
566 if ((nbi > MAX_HASH_LEN) &&
567 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
568 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
569 /* Note that entry may have been freed at this point by xmlDictGrow */
570
571 return(ret);
572}
573
574/**
Daniel Veillard81514ba2003-09-16 23:17:26 +0000575 * xmlDictOwns:
576 * @dict: the dictionnary
577 * @str: the string
578 *
579 * check if a string is owned by the disctionary
580 *
581 * Returns 1 if true, 0 if false and -1 in case of error
582 * -1 in case of error
583 */
584int
585xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
586 xmlDictStringsPtr pool;
587
588 if ((dict == NULL) || (str == NULL))
589 return(-1);
590 pool = dict->strings;
591 while (pool != NULL) {
592 if ((str >= pool->array) && (str <= pool->free))
593 return(1);
594 pool = pool->next;
595 }
596 return(0);
597}
598/**
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000599 * xmlDictSize:
600 * @dict: the dictionnary
601 *
602 * Query the number of elements installed in the hash @dict.
603 *
604 * Returns the number of elements in the dictionnary or
605 * -1 in case of error
606 */
607int
608xmlDictSize(xmlDictPtr dict) {
609 if (dict == NULL)
610 return(-1);
611 return(dict->nbElems);
612}
613
614