blob: 20d1efb5d17665ebc7ccbfabf9ab35cb8925a829 [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 Veillard2fdbd322003-08-18 12:15:38 +0000118 * xmlDictComputeKey:
119 * Calculate the hash key
120 */
121static unsigned long
122xmlDictComputeKey(xmlDictPtr dict, const xmlChar *name, int namelen) {
123 unsigned long value = 0L;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000124
125 if (name == NULL) return(0);
126 value += 30 * (*name);
127 if (namelen > 10) {
128 value += name[namelen - 1];
129 namelen = 10;
130 }
131 switch (namelen) {
132 case 10: value += name[9];
133 case 9: value += name[8];
134 case 8: value += name[7];
135 case 7: value += name[6];
136 case 6: value += name[5];
137 case 5: value += name[4];
138 case 4: value += name[3];
139 case 3: value += name[2];
140 case 2: value += name[1];
141 case 1: value += name[0];
142 default: break;
143 }
144#if 0
145 while ((len++ < namelen) && ((ch = *name++) != 0)) {
146 value += (unsigned long)ch;
147 }
148#endif
149#if 0
150 while ((len++ < namelen) && ((ch = *name++) != 0)) {
151 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
152 }
153#endif
154 return (value % dict->size);
155}
156
157/**
158 * xmlDictCreate:
159 *
160 * Create a new dictionary
161 *
162 * Returns the newly created object, or NULL if an error occured.
163 */
164xmlDictPtr
165xmlDictCreate(void) {
166 xmlDictPtr dict;
167
168 dict = xmlMalloc(sizeof(xmlDict));
169 if (dict) {
170 dict->size = MIN_DICT_SIZE;
171 dict->nbElems = 0;
172 dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
Daniel Veillard81514ba2003-09-16 23:17:26 +0000173 dict->strings = NULL;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000174 if (dict->dict) {
175 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
176 return(dict);
177 }
178 xmlFree(dict);
179 }
180 return(NULL);
181}
182
183/**
184 * xmlDictGrow:
185 * @dict: the dictionnary
186 * @size: the new size of the dictionnary
187 *
188 * resize the dictionnary
189 *
190 * Returns 0 in case of success, -1 in case of failure
191 */
192static int
193xmlDictGrow(xmlDictPtr dict, int size) {
194 unsigned long key;
195 int oldsize, i;
196 xmlDictEntryPtr iter, next;
197 struct _xmlDictEntry *olddict;
198#ifdef DEBUG_GROW
199 unsigned long nbElem = 0;
200#endif
201
202 if (dict == NULL)
203 return(-1);
204 if (size < 8)
205 return(-1);
206 if (size > 8 * 2048)
207 return(-1);
208
209 oldsize = dict->size;
210 olddict = dict->dict;
211 if (olddict == NULL)
212 return(-1);
213
214 dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
215 if (dict->dict == NULL) {
216 dict->dict = olddict;
217 return(-1);
218 }
219 memset(dict->dict, 0, size * sizeof(xmlDictEntry));
220 dict->size = size;
221
222 /* If the two loops are merged, there would be situations where
223 a new entry needs to allocated and data copied into it from
224 the main dict. So instead, we run through the array twice, first
225 copying all the elements in the main array (where we can't get
226 conflicts) and then the rest, so we only free (and don't allocate)
227 */
228 for (i = 0; i < oldsize; i++) {
229 if (olddict[i].valid == 0)
230 continue;
231 key = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
232 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
233 dict->dict[key].next = NULL;
234#ifdef DEBUG_GROW
235 nbElem++;
236#endif
237 }
238
239 for (i = 0; i < oldsize; i++) {
240 iter = olddict[i].next;
241 while (iter) {
242 next = iter->next;
243
244 /*
245 * put back the entry in the new dict
246 */
247
248 key = xmlDictComputeKey(dict, iter->name, iter->len);
249 if (dict->dict[key].valid == 0) {
250 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
251 dict->dict[key].next = NULL;
252 dict->dict[key].valid = 1;
253 xmlFree(iter);
254 } else {
255 iter->next = dict->dict[key].next;
256 dict->dict[key].next = iter;
257 }
258
259#ifdef DEBUG_GROW
260 nbElem++;
261#endif
262
263 iter = next;
264 }
265 }
266
267 xmlFree(olddict);
268
269#ifdef DEBUG_GROW
270 xmlGenericError(xmlGenericErrorContext,
271 "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
272#endif
273
274 return(0);
275}
276
277/**
278 * xmlDictFree:
279 * @dict: the dictionnary
280 *
281 * Free the hash @dict and its contents. The userdata is
282 * deallocated with @f if provided.
283 */
284void
285xmlDictFree(xmlDictPtr dict) {
286 int i;
287 xmlDictEntryPtr iter;
288 xmlDictEntryPtr next;
289 int inside_dict = 0;
Daniel Veillard81514ba2003-09-16 23:17:26 +0000290 xmlDictStringsPtr pool, nextp;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000291
292 if (dict == NULL)
293 return;
294 if (dict->dict) {
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000295 for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) {
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000296 iter = &(dict->dict[i]);
297 if (iter->valid == 0)
298 continue;
299 inside_dict = 1;
300 while (iter) {
301 next = iter->next;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000302 if (!inside_dict)
303 xmlFree(iter);
Daniel Veillard6155d8a2003-08-19 15:01:28 +0000304 dict->nbElems--;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000305 inside_dict = 0;
306 iter = next;
307 }
308 inside_dict = 0;
309 }
310 xmlFree(dict->dict);
311 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000312 pool = dict->strings;
313 while (pool != NULL) {
314 nextp = pool->next;
315 xmlFree(pool);
316 pool = nextp;
317 }
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000318 xmlFree(dict);
319}
320
321/**
322 * xmlDictLookup:
323 * @dict: the dictionnary
324 * @name: the name of the userdata
Daniel Veillard0fb18932003-09-07 09:14:37 +0000325 * @len: the length of the name, if -1 it is recomputed
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000326 *
327 * Add the @name to the hash @dict if not present.
328 *
329 * Returns the internal copy of the name or NULL in case of internal error
330 */
331const xmlChar *
332xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
333 unsigned long key, nbi = 0;
334 xmlDictEntryPtr entry;
335 xmlDictEntryPtr insert;
336 const xmlChar *ret;
337
Daniel Veillard0fb18932003-09-07 09:14:37 +0000338 if ((dict == NULL) || (name == NULL))
339 return(NULL);
340
341 if (len < 0)
342 len = xmlStrlen(name);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000343
344 /*
345 * Check for duplicate and insertion location.
346 */
347 key = xmlDictComputeKey(dict, name, len);
348 if (dict->dict[key].valid == 0) {
349 insert = NULL;
350 } else {
351 for (insert = &(dict->dict[key]); insert->next != NULL;
352 insert = insert->next) {
353 if ((insert->len == len) &&
354 (!xmlStrncmp(insert->name, name, len)))
355 return(insert->name);
356 nbi++;
357 }
358 if ((insert->len == len) &&
359 (!xmlStrncmp(insert->name, name, len)))
360 return(insert->name);
361 }
362
Daniel Veillard81514ba2003-09-16 23:17:26 +0000363 ret = xmlDictAddString(dict, name, len);
364 if (ret == NULL)
365 return(NULL);
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000366 if (insert == NULL) {
367 entry = &(dict->dict[key]);
368 } else {
369 entry = xmlMalloc(sizeof(xmlDictEntry));
370 if (entry == NULL)
371 return(NULL);
372 }
Daniel Veillard81514ba2003-09-16 23:17:26 +0000373 entry->name = ret;
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000374 entry->len = len;
375 entry->next = NULL;
376 entry->valid = 1;
377
378
379 if (insert != NULL)
380 insert->next = entry;
381
382 dict->nbElems++;
383
384 if ((nbi > MAX_HASH_LEN) &&
385 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
386 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
387 /* Note that entry may have been freed at this point by xmlDictGrow */
388
389 return(ret);
390}
391
392/**
Daniel Veillard81514ba2003-09-16 23:17:26 +0000393 * xmlDictOwns:
394 * @dict: the dictionnary
395 * @str: the string
396 *
397 * check if a string is owned by the disctionary
398 *
399 * Returns 1 if true, 0 if false and -1 in case of error
400 * -1 in case of error
401 */
402int
403xmlDictOwns(xmlDictPtr dict, const xmlChar *str) {
404 xmlDictStringsPtr pool;
405
406 if ((dict == NULL) || (str == NULL))
407 return(-1);
408 pool = dict->strings;
409 while (pool != NULL) {
410 if ((str >= pool->array) && (str <= pool->free))
411 return(1);
412 pool = pool->next;
413 }
414 return(0);
415}
416/**
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000417 * xmlDictSize:
418 * @dict: the dictionnary
419 *
420 * Query the number of elements installed in the hash @dict.
421 *
422 * Returns the number of elements in the dictionnary or
423 * -1 in case of error
424 */
425int
426xmlDictSize(xmlDictPtr dict) {
427 if (dict == NULL)
428 return(-1);
429 return(dict->nbElems);
430}
431
432