blob: 1002234058d14dd8a1ce8a7da2fefd6ff894d3e4 [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;
43 xmlChar *name;
44 int len;
45 int valid;
46};
47
48/*
49 * The entire dictionnary
50 */
51struct _xmlDict {
52 struct _xmlDictEntry *dict;
53 int size;
54 int nbElems;
55};
56
57/*
58 * xmlDictComputeKey:
59 * Calculate the hash key
60 */
61static unsigned long
62xmlDictComputeKey(xmlDictPtr dict, const xmlChar *name, int namelen) {
63 unsigned long value = 0L;
64 char ch;
65 int len = 0;
66
67 if (name == NULL) return(0);
68 value += 30 * (*name);
69 if (namelen > 10) {
70 value += name[namelen - 1];
71 namelen = 10;
72 }
73 switch (namelen) {
74 case 10: value += name[9];
75 case 9: value += name[8];
76 case 8: value += name[7];
77 case 7: value += name[6];
78 case 6: value += name[5];
79 case 5: value += name[4];
80 case 4: value += name[3];
81 case 3: value += name[2];
82 case 2: value += name[1];
83 case 1: value += name[0];
84 default: break;
85 }
86#if 0
87 while ((len++ < namelen) && ((ch = *name++) != 0)) {
88 value += (unsigned long)ch;
89 }
90#endif
91#if 0
92 while ((len++ < namelen) && ((ch = *name++) != 0)) {
93 value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch);
94 }
95#endif
96 return (value % dict->size);
97}
98
99/**
100 * xmlDictCreate:
101 *
102 * Create a new dictionary
103 *
104 * Returns the newly created object, or NULL if an error occured.
105 */
106xmlDictPtr
107xmlDictCreate(void) {
108 xmlDictPtr dict;
109
110 dict = xmlMalloc(sizeof(xmlDict));
111 if (dict) {
112 dict->size = MIN_DICT_SIZE;
113 dict->nbElems = 0;
114 dict->dict = xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry));
115 if (dict->dict) {
116 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry));
117 return(dict);
118 }
119 xmlFree(dict);
120 }
121 return(NULL);
122}
123
124/**
125 * xmlDictGrow:
126 * @dict: the dictionnary
127 * @size: the new size of the dictionnary
128 *
129 * resize the dictionnary
130 *
131 * Returns 0 in case of success, -1 in case of failure
132 */
133static int
134xmlDictGrow(xmlDictPtr dict, int size) {
135 unsigned long key;
136 int oldsize, i;
137 xmlDictEntryPtr iter, next;
138 struct _xmlDictEntry *olddict;
139#ifdef DEBUG_GROW
140 unsigned long nbElem = 0;
141#endif
142
143 if (dict == NULL)
144 return(-1);
145 if (size < 8)
146 return(-1);
147 if (size > 8 * 2048)
148 return(-1);
149
150 oldsize = dict->size;
151 olddict = dict->dict;
152 if (olddict == NULL)
153 return(-1);
154
155 dict->dict = xmlMalloc(size * sizeof(xmlDictEntry));
156 if (dict->dict == NULL) {
157 dict->dict = olddict;
158 return(-1);
159 }
160 memset(dict->dict, 0, size * sizeof(xmlDictEntry));
161 dict->size = size;
162
163 /* If the two loops are merged, there would be situations where
164 a new entry needs to allocated and data copied into it from
165 the main dict. So instead, we run through the array twice, first
166 copying all the elements in the main array (where we can't get
167 conflicts) and then the rest, so we only free (and don't allocate)
168 */
169 for (i = 0; i < oldsize; i++) {
170 if (olddict[i].valid == 0)
171 continue;
172 key = xmlDictComputeKey(dict, olddict[i].name, olddict[i].len);
173 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry));
174 dict->dict[key].next = NULL;
175#ifdef DEBUG_GROW
176 nbElem++;
177#endif
178 }
179
180 for (i = 0; i < oldsize; i++) {
181 iter = olddict[i].next;
182 while (iter) {
183 next = iter->next;
184
185 /*
186 * put back the entry in the new dict
187 */
188
189 key = xmlDictComputeKey(dict, iter->name, iter->len);
190 if (dict->dict[key].valid == 0) {
191 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry));
192 dict->dict[key].next = NULL;
193 dict->dict[key].valid = 1;
194 xmlFree(iter);
195 } else {
196 iter->next = dict->dict[key].next;
197 dict->dict[key].next = iter;
198 }
199
200#ifdef DEBUG_GROW
201 nbElem++;
202#endif
203
204 iter = next;
205 }
206 }
207
208 xmlFree(olddict);
209
210#ifdef DEBUG_GROW
211 xmlGenericError(xmlGenericErrorContext,
212 "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
213#endif
214
215 return(0);
216}
217
218/**
219 * xmlDictFree:
220 * @dict: the dictionnary
221 *
222 * Free the hash @dict and its contents. The userdata is
223 * deallocated with @f if provided.
224 */
225void
226xmlDictFree(xmlDictPtr dict) {
227 int i;
228 xmlDictEntryPtr iter;
229 xmlDictEntryPtr next;
230 int inside_dict = 0;
231
232 if (dict == NULL)
233 return;
234 if (dict->dict) {
235 for(i = 0; i < dict->size; i++) {
236 iter = &(dict->dict[i]);
237 if (iter->valid == 0)
238 continue;
239 inside_dict = 1;
240 while (iter) {
241 next = iter->next;
242 if (iter->name)
243 xmlFree(iter->name);
244 if (!inside_dict)
245 xmlFree(iter);
246 inside_dict = 0;
247 iter = next;
248 }
249 inside_dict = 0;
250 }
251 xmlFree(dict->dict);
252 }
253 xmlFree(dict);
254}
255
256/**
257 * xmlDictLookup:
258 * @dict: the dictionnary
259 * @name: the name of the userdata
260 * @len: the length of the name
261 *
262 * Add the @name to the hash @dict if not present.
263 *
264 * Returns the internal copy of the name or NULL in case of internal error
265 */
266const xmlChar *
267xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) {
268 unsigned long key, nbi = 0;
269 xmlDictEntryPtr entry;
270 xmlDictEntryPtr insert;
271 const xmlChar *ret;
272
273 if ((dict == NULL) || (name == NULL) || (len <= 0))
274 return(NULL);
275
276 /*
277 * Check for duplicate and insertion location.
278 */
279 key = xmlDictComputeKey(dict, name, len);
280 if (dict->dict[key].valid == 0) {
281 insert = NULL;
282 } else {
283 for (insert = &(dict->dict[key]); insert->next != NULL;
284 insert = insert->next) {
285 if ((insert->len == len) &&
286 (!xmlStrncmp(insert->name, name, len)))
287 return(insert->name);
288 nbi++;
289 }
290 if ((insert->len == len) &&
291 (!xmlStrncmp(insert->name, name, len)))
292 return(insert->name);
293 }
294
295 if (insert == NULL) {
296 entry = &(dict->dict[key]);
297 } else {
298 entry = xmlMalloc(sizeof(xmlDictEntry));
299 if (entry == NULL)
300 return(NULL);
301 }
302
303 ret = entry->name = xmlStrndup(name, len);
304 entry->len = len;
305 entry->next = NULL;
306 entry->valid = 1;
307
308
309 if (insert != NULL)
310 insert->next = entry;
311
312 dict->nbElems++;
313
314 if ((nbi > MAX_HASH_LEN) &&
315 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN)))
316 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size);
317 /* Note that entry may have been freed at this point by xmlDictGrow */
318
319 return(ret);
320}
321
322/**
323 * xmlDictSize:
324 * @dict: the dictionnary
325 *
326 * Query the number of elements installed in the hash @dict.
327 *
328 * Returns the number of elements in the dictionnary or
329 * -1 in case of error
330 */
331int
332xmlDictSize(xmlDictPtr dict) {
333 if (dict == NULL)
334 return(-1);
335 return(dict->nbElems);
336}
337
338