|
1 /* |
|
2 * libxml2_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 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
18 */ |
|
19 |
|
20 #define IN_LIBXML |
|
21 #undef XE_ENABLE_GS_CACHING |
|
22 #include "xmlenglibxml.h" |
|
23 |
|
24 #include <string.h> |
|
25 #include <stdapis/libxml2/libxml2_globals.h> |
|
26 |
|
27 #define MAX_HASH_LEN 4 |
|
28 |
|
29 |
|
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 * xmlDictAddString: |
|
38 * @param dict the dictionnary |
|
39 * @param name the name of the userdata |
|
40 * @param len the length of the name, if -1 it is recomputed |
|
41 * |
|
42 * Add the string to the array[s] |
|
43 * |
|
44 * Returns the pointer of the local string, or NULL in case of error. |
|
45 * |
|
46 * OOM: possible --> NULL is returned, OOM flag is set |
|
47 */ |
|
48 static const xmlChar* |
|
49 xmlDictAddString(xmlDictPtr dict, const xmlChar *name, int namelen) { |
|
50 xmlDictStringsPtr pool; |
|
51 const xmlChar *ret; |
|
52 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ |
|
53 |
|
54 pool = dict->strings; |
|
55 while (pool) { |
|
56 if (pool->end - pool->free > namelen) |
|
57 goto found_pool; |
|
58 if (pool->size > size) size = pool->size; |
|
59 pool = pool->next; |
|
60 } |
|
61 /* |
|
62 * Not found, need to allocate |
|
63 */ |
|
64 if (!pool) { |
|
65 if (size == 0) |
|
66 size = 1000; |
|
67 else |
|
68 size *= 4; /* exponential growth */ |
|
69 |
|
70 if (size < 4 * namelen) |
|
71 size = 4 * namelen; /* just in case ! */ |
|
72 |
|
73 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); |
|
74 if (!pool) |
|
75 return(NULL); // OOM |
|
76 pool->size = size; |
|
77 pool->nbStrings = 0; |
|
78 pool->free = &pool->array[0]; |
|
79 pool->end = &pool->array[size]; |
|
80 pool->next = dict->strings; |
|
81 dict->strings = pool; |
|
82 } |
|
83 found_pool: |
|
84 ret = pool->free; |
|
85 memcpy(pool->free, name, namelen); |
|
86 pool->free += namelen; |
|
87 *(pool->free++) = 0; |
|
88 return(ret); |
|
89 } |
|
90 |
|
91 /* |
|
92 * xmlDictAddQString: |
|
93 * @param dict the dictionnary |
|
94 * @param prefix the prefix of the userdata |
|
95 * @param name the name of the userdata |
|
96 * @param len the length of the name, if -1 it is recomputed |
|
97 * |
|
98 * Add the QName to the array[s] |
|
99 * |
|
100 * Returns the pointer of the local string, or NULL in case of error. |
|
101 */ |
|
102 static const xmlChar * |
|
103 xmlDictAddQString(xmlDictPtr dict, const xmlChar *prefix, |
|
104 const xmlChar *name, int namelen) |
|
105 { |
|
106 xmlDictStringsPtr pool; |
|
107 const xmlChar *ret; |
|
108 int size = 0; /* + sizeof(_xmlDictStrings) == 1024 */ |
|
109 int plen; |
|
110 |
|
111 if (!prefix) |
|
112 return(xmlDictAddString(dict, name, namelen)); |
|
113 plen = xmlStrlen(prefix); |
|
114 |
|
115 pool = dict->strings; |
|
116 while (pool) { |
|
117 if (pool->end - pool->free > namelen) |
|
118 goto found_pool; |
|
119 if (pool->size > size) |
|
120 size = pool->size; |
|
121 pool = pool->next; |
|
122 } |
|
123 /* |
|
124 * Not found, need to allocate |
|
125 */ |
|
126 if (!pool) { |
|
127 if (size == 0) |
|
128 size = 1000; |
|
129 else |
|
130 size *= 4; /* exponential growth */ |
|
131 if (size < 4 * namelen) |
|
132 size = 4 * namelen; /* just in case ! */ |
|
133 pool = (xmlDictStringsPtr) xmlMalloc(sizeof(xmlDictStrings) + size); |
|
134 if (!pool) |
|
135 return(NULL); |
|
136 pool->size = size; |
|
137 pool->nbStrings = 0; |
|
138 pool->free = &pool->array[0]; |
|
139 pool->end = &pool->array[size]; |
|
140 pool->next = dict->strings; |
|
141 dict->strings = pool; |
|
142 } |
|
143 found_pool: |
|
144 ret = pool->free; |
|
145 memcpy(pool->free, prefix, plen); |
|
146 pool->free += plen; |
|
147 *(pool->free++) = ':'; |
|
148 namelen -= plen + 1; |
|
149 memcpy(pool->free, name, namelen); |
|
150 pool->free += namelen; |
|
151 *(pool->free++) = 0; |
|
152 return(ret); |
|
153 } |
|
154 |
|
155 /* |
|
156 * xmlDictComputeKey: |
|
157 * Calculate the hash key |
|
158 * |
|
159 * OOM: never |
|
160 */ |
|
161 |
|
162 |
|
163 static unsigned long |
|
164 xmlDictComputeKey(const xmlChar *name, int namelen) { |
|
165 unsigned long value = 0L; |
|
166 |
|
167 if (!name) |
|
168 return(0); |
|
169 value = *name; |
|
170 value <<= 5; |
|
171 if (namelen > 10) { |
|
172 value += name[namelen - 1]; |
|
173 namelen = 10; |
|
174 } |
|
175 // DONE: OPTIMIZE: Will it be better to rewrite SWITCH as IF? : |
|
176 // if(namelen > 1) // namelen by this point is <= 10 |
|
177 // value+=name[namelen-1] |
|
178 // |
|
179 if (namelen > 1) |
|
180 value += name[namelen-1]; |
|
181 /* |
|
182 switch (namelen) { |
|
183 case 10: value += name[9]; |
|
184 case 9: value += name[8]; |
|
185 case 8: value += name[7]; |
|
186 case 7: value += name[6]; |
|
187 case 6: value += name[5]; |
|
188 case 5: value += name[4]; |
|
189 case 4: value += name[3]; |
|
190 case 3: value += name[2]; |
|
191 case 2: value += name[1]; |
|
192 default: break; |
|
193 } |
|
194 */ |
|
195 return(value); |
|
196 } |
|
197 |
|
198 /* |
|
199 * xmlDictComputeQKey: |
|
200 * Calculate the hash key |
|
201 */ |
|
202 static unsigned long |
|
203 xmlDictComputeQKey(const xmlChar *prefix, const xmlChar *name, int len) |
|
204 { |
|
205 unsigned long value; // = 0L; //avoided initialization |
|
206 int plen; |
|
207 |
|
208 if (!prefix) |
|
209 return(xmlDictComputeKey(name, len)); |
|
210 |
|
211 plen = xmlStrlen(prefix); |
|
212 value = 30 * (plen ? *prefix : (unsigned long) ':'); |
|
213 |
|
214 if (len > 10) { |
|
215 value += name[len - (plen + 1 + 1)]; |
|
216 len = 10; |
|
217 if (plen > 10) |
|
218 plen = 10; |
|
219 } |
|
220 |
|
221 // DONE: OPTIMIZE: see "Optimize-It" note in xmlDictComputeKey |
|
222 /* |
|
223 switch (plen) { |
|
224 case 10: value += prefix[9]; |
|
225 case 9: value += prefix[8]; |
|
226 case 8: value += prefix[7]; |
|
227 case 7: value += prefix[6]; |
|
228 case 6: value += prefix[5]; |
|
229 case 5: value += prefix[4]; |
|
230 case 4: value += prefix[3]; |
|
231 case 3: value += prefix[2]; |
|
232 case 2: value += prefix[1]; |
|
233 case 1: value += prefix[0]; |
|
234 default: break; |
|
235 } |
|
236 */ |
|
237 // REPLACED WITH |
|
238 if (plen < 11 && plen >0) |
|
239 value += prefix[plen-1]; |
|
240 //------------------------------------- |
|
241 len -= plen; |
|
242 if (len > 0) { |
|
243 value += (unsigned long) ':'; |
|
244 len--; |
|
245 } |
|
246 /* --- REPLACED |
|
247 switch (len) { |
|
248 case 10: value += name[9]; |
|
249 case 9: value += name[8]; |
|
250 case 8: value += name[7]; |
|
251 case 7: value += name[6]; |
|
252 case 6: value += name[5]; |
|
253 case 5: value += name[4]; |
|
254 case 4: value += name[3]; |
|
255 case 3: value += name[2]; |
|
256 case 2: value += name[1]; |
|
257 case 1: value += name[0]; |
|
258 default: break; |
|
259 } |
|
260 */ |
|
261 //-- REPLACED WITH |
|
262 if(len > 0 && len < 11) |
|
263 value += name[len-1]; |
|
264 //-------------------- |
|
265 return(value); |
|
266 } |
|
267 |
|
268 /** |
|
269 * xmlDictCreate: |
|
270 * |
|
271 * Create a new dictionary |
|
272 * |
|
273 * Returns the newly created dictionnary, or NULL if an error occured. |
|
274 * |
|
275 * OOM: possible --> NULL is returned; OOM flag is set |
|
276 */ |
|
277 XMLPUBFUNEXPORT xmlDictPtr |
|
278 xmlDictCreate(void) { |
|
279 xmlDictPtr dict; |
|
280 |
|
281 dict = (xmlDictPtr)xmlMalloc(sizeof(xmlDict)); |
|
282 if (dict) { |
|
283 dict->ref_counter = 1; |
|
284 |
|
285 dict->size = MIN_DICT_SIZE; |
|
286 dict->nbElems = 0; |
|
287 dict->dict = (xmlDictEntryPtr)xmlMalloc(MIN_DICT_SIZE * sizeof(xmlDictEntry)); |
|
288 dict->strings = NULL; |
|
289 dict->subdict = NULL; |
|
290 if (dict->dict) { |
|
291 memset(dict->dict, 0, MIN_DICT_SIZE * sizeof(xmlDictEntry)); |
|
292 return(dict); |
|
293 } |
|
294 xmlFree(dict); // OOM |
|
295 } |
|
296 return(NULL); // OOM |
|
297 } |
|
298 |
|
299 /** |
|
300 * xmlDictCreateSub: |
|
301 * @param sub an existing dictionnary |
|
302 * |
|
303 * Create a new dictionary, inheriting strings from the read-only |
|
304 * dictionnary sub. On lookup, strings are first searched in the |
|
305 * new dictionnary, then in sub, and if not found are created in the |
|
306 * new dictionnary. |
|
307 * |
|
308 * Returns the newly created dictionnary, or NULL if an error occured. |
|
309 */ |
|
310 XMLPUBFUNEXPORT xmlDictPtr |
|
311 xmlDictCreateSub(xmlDictPtr sub) { |
|
312 xmlDictPtr dict = xmlDictCreate(); |
|
313 |
|
314 if (dict && sub) { |
|
315 dict->subdict = sub; |
|
316 xmlDictReference(dict->subdict); |
|
317 } |
|
318 return(dict); |
|
319 } |
|
320 |
|
321 /** |
|
322 * xmlDictReference: |
|
323 * @param dict the dictionnary |
|
324 * |
|
325 * Increment the reference counter of a dictionary |
|
326 * |
|
327 * Returns 0 in case of success and -1 in case of error |
|
328 * |
|
329 * OOM: never |
|
330 */ |
|
331 XMLPUBFUNEXPORT int |
|
332 xmlDictReference(xmlDictPtr dict) { |
|
333 if (!dict) |
|
334 return -1; |
|
335 dict->ref_counter++; |
|
336 return(0); |
|
337 } |
|
338 |
|
339 /** |
|
340 * xmlDictGrow: |
|
341 * @param dict the dictionnary |
|
342 * @param size the new size of the dictionnary |
|
343 * |
|
344 * resize the dictionnary |
|
345 * |
|
346 * Returns 0 in case of success, -1 in case of failure |
|
347 * |
|
348 * OOM: possible --> returns -1, OOM is set |
|
349 */ |
|
350 static int |
|
351 xmlDictGrow(xmlDictPtr dict, int size) { |
|
352 unsigned long key; |
|
353 int oldsize, i; |
|
354 xmlDictEntryPtr iter, next; |
|
355 struct _xmlDictEntry *olddict; |
|
356 #ifdef DEBUG_GROW |
|
357 unsigned long nbElem = 0; |
|
358 #endif |
|
359 |
|
360 if (!dict || size < 8 || size > 8 * 2048) |
|
361 return(-1); |
|
362 |
|
363 oldsize = dict->size; |
|
364 olddict = dict->dict; |
|
365 if (!olddict) |
|
366 return(-1); |
|
367 |
|
368 dict->dict = (xmlDictEntryPtr)xmlMalloc(size * sizeof(xmlDictEntry)); |
|
369 if (!dict->dict) { |
|
370 dict->dict = olddict; |
|
371 return(-1); |
|
372 } |
|
373 memset(dict->dict, 0, size * sizeof(xmlDictEntry)); |
|
374 dict->size = size; |
|
375 |
|
376 /* If the two loops are merged, there would be situations where |
|
377 a new entry needs to allocated and data copied into it from |
|
378 the main dict. So instead, we run through the array twice, first |
|
379 copying all the elements in the main array (where we can't get |
|
380 conflicts) and then the rest, so we only free (and don't allocate) |
|
381 */ |
|
382 for (i = 0; i < oldsize; i++) |
|
383 { |
|
384 if (olddict[i].valid == 0) |
|
385 continue; |
|
386 key = xmlDictComputeKey(olddict[i].name, olddict[i].len) % dict->size; |
|
387 memcpy(&(dict->dict[key]), &(olddict[i]), sizeof(xmlDictEntry)); |
|
388 dict->dict[key].next = NULL; |
|
389 #ifdef DEBUG_GROW |
|
390 nbElem++; |
|
391 #endif |
|
392 } |
|
393 |
|
394 for (i = 0; i < oldsize; i++) |
|
395 { |
|
396 iter = olddict[i].next; |
|
397 while (iter) { |
|
398 next = iter->next; |
|
399 /* |
|
400 * put back the entry in the new dict |
|
401 */ |
|
402 key = xmlDictComputeKey(iter->name, iter->len) % dict->size; |
|
403 if (dict->dict[key].valid == 0) { |
|
404 memcpy(&(dict->dict[key]), iter, sizeof(xmlDictEntry)); |
|
405 dict->dict[key].next = NULL; |
|
406 dict->dict[key].valid = 1; |
|
407 xmlFree(iter); |
|
408 } else { |
|
409 iter->next = dict->dict[key].next; |
|
410 dict->dict[key].next = iter; |
|
411 } |
|
412 #ifdef DEBUG_GROW |
|
413 nbElem++; |
|
414 #endif |
|
415 iter = next; |
|
416 } // while (iter) |
|
417 } // for (i = 0; i < oldsize; i++) |
|
418 |
|
419 xmlFree(olddict); |
|
420 |
|
421 #ifdef DEBUG_GROW |
|
422 xmlGenericError(xmlGenericErrorContext, |
|
423 "xmlDictGrow : from %d to %d, %d elems\n", oldsize, size, nbElem); |
|
424 #endif |
|
425 |
|
426 return(0); |
|
427 } |
|
428 |
|
429 /** |
|
430 * xmlDictFree: |
|
431 * @param dict the dictionnary |
|
432 * |
|
433 * Free the hash dict and its contents. The userdata is |
|
434 * deallocated with f if provided. |
|
435 */ |
|
436 XMLPUBFUNEXPORT void |
|
437 xmlDictFree(xmlDictPtr dict) |
|
438 { |
|
439 int i; |
|
440 xmlDictEntryPtr iter; |
|
441 xmlDictEntryPtr next; |
|
442 int inside_dict = 0; |
|
443 xmlDictStringsPtr pool, nextp; |
|
444 |
|
445 if (!dict) |
|
446 return; |
|
447 |
|
448 /* decrement the counter, it may be shared by a parser and docs */ |
|
449 dict->ref_counter--; |
|
450 if (dict->ref_counter > 0) |
|
451 return; |
|
452 |
|
453 if (dict->subdict) { |
|
454 xmlDictFree(dict->subdict); |
|
455 } |
|
456 |
|
457 if (dict->dict) { |
|
458 for(i = 0; ((i < dict->size) && (dict->nbElems > 0)); i++) |
|
459 { |
|
460 iter = &(dict->dict[i]); |
|
461 if (iter->valid == 0) |
|
462 continue; |
|
463 inside_dict = 1; |
|
464 while (iter) |
|
465 { |
|
466 next = iter->next; |
|
467 if (!inside_dict) |
|
468 xmlFree(iter); |
|
469 dict->nbElems--; |
|
470 inside_dict = 0; |
|
471 iter = next; |
|
472 } |
|
473 inside_dict = 0; |
|
474 } |
|
475 xmlFree(dict->dict); |
|
476 } |
|
477 |
|
478 pool = dict->strings; |
|
479 while (pool) { |
|
480 nextp = pool->next; |
|
481 xmlFree(pool); |
|
482 pool = nextp; |
|
483 } |
|
484 xmlFree(dict); |
|
485 } |
|
486 |
|
487 /** |
|
488 * xmlDictLookup: |
|
489 * @param dict the dictionnary |
|
490 * @param name the name of the userdata |
|
491 * @param len the length of the name, if -1 it is recomputed |
|
492 * |
|
493 * Add the name to the hash dict if not present. |
|
494 * |
|
495 * Returns the internal copy of the name or NULL in case of internal error |
|
496 * |
|
497 * OOM: possible --> returns NULL and OOM flag is set |
|
498 */ |
|
499 XMLPUBFUNEXPORT const xmlChar* |
|
500 xmlDictLookup(xmlDictPtr dict, const xmlChar *name, int len) { |
|
501 unsigned long key, okey, nbi = 0; |
|
502 xmlDictEntryPtr entry; |
|
503 xmlDictEntryPtr insert; |
|
504 const xmlChar *ret; |
|
505 |
|
506 if (!dict || !name) |
|
507 return(NULL); |
|
508 |
|
509 if (len < 0) |
|
510 len = xmlStrlen(name); |
|
511 |
|
512 /* |
|
513 * Check for duplicate and insertion location. |
|
514 */ |
|
515 okey = xmlDictComputeKey(name, len); |
|
516 key = okey % dict->size; |
|
517 if (dict->dict[key].valid == 0) { |
|
518 insert = NULL; |
|
519 } else { |
|
520 for (insert = &(dict->dict[key]); |
|
521 insert->next; |
|
522 insert = insert->next) |
|
523 { |
|
524 |
|
525 #ifdef __GNUC__ |
|
526 if (insert->len == len) { |
|
527 if (!memcmp(insert->name, name, len)) |
|
528 return(insert->name); |
|
529 } |
|
530 #else |
|
531 if ((insert->len == len) && (!xmlStrncmp(insert->name, name, len))) |
|
532 return(insert->name); |
|
533 #endif |
|
534 nbi++; |
|
535 } |
|
536 #ifdef __GNUC__ |
|
537 if (insert->len == len) { |
|
538 if (!memcmp(insert->name, name, len)) |
|
539 return(insert->name); |
|
540 } |
|
541 #else |
|
542 if ((insert->len == len) && (!xmlStrncmp(insert->name, name, len))) |
|
543 return(insert->name); |
|
544 #endif |
|
545 } |
|
546 |
|
547 if (dict->subdict) { |
|
548 key = okey % dict->subdict->size; |
|
549 if (dict->subdict->dict[key].valid != 0) { |
|
550 xmlDictEntryPtr tmp; |
|
551 |
|
552 for (tmp = &(dict->subdict->dict[key]); tmp->next != NULL; tmp = tmp->next) |
|
553 { |
|
554 #ifdef __GNUC__ |
|
555 if (tmp->len == len) { |
|
556 if (!memcmp(tmp->name, name, len)) |
|
557 return(tmp->name); |
|
558 } |
|
559 #else |
|
560 if ((tmp->len == len) && (!xmlStrncmp(tmp->name, name, len))) |
|
561 return(tmp->name); |
|
562 #endif |
|
563 nbi++; |
|
564 } |
|
565 #ifdef __GNUC__ |
|
566 if (tmp->len == len) { |
|
567 if (!memcmp(tmp->name, name, len)) |
|
568 return(tmp->name); |
|
569 } |
|
570 #else |
|
571 if ((tmp->len == len) && (!xmlStrncmp(tmp->name, name, len))) |
|
572 return(tmp->name); |
|
573 #endif |
|
574 } // if (dict->subdict->dict[key].valid != 0) |
|
575 |
|
576 key = okey % dict->size; |
|
577 } // if (dict->subdict) |
|
578 |
|
579 ret = xmlDictAddString(dict, name, len); // may set OOM flag |
|
580 if (!ret) |
|
581 return(NULL); |
|
582 |
|
583 if (!insert) { |
|
584 entry = &(dict->dict[key]); |
|
585 } else { |
|
586 entry = (xmlDictEntryPtr)xmlMalloc(sizeof(xmlDictEntry)); |
|
587 if (!entry) |
|
588 return(NULL); //OOM |
|
589 } |
|
590 entry->name = ret; |
|
591 entry->len = len; |
|
592 entry->next = NULL; |
|
593 entry->valid = 1; |
|
594 |
|
595 if (insert) |
|
596 insert->next = entry; |
|
597 |
|
598 dict->nbElems++; |
|
599 |
|
600 if ((nbi > MAX_HASH_LEN) && |
|
601 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) |
|
602 { |
|
603 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size); |
|
604 if(OOM_FLAG) |
|
605 return NULL; |
|
606 } |
|
607 |
|
608 /* Note that entry may have been freed at this point by xmlDictGrow */ |
|
609 |
|
610 return(ret); |
|
611 } |
|
612 |
|
613 /** |
|
614 * xmlDictQLookup: |
|
615 * @param dict the dictionnary |
|
616 * @param prefix the prefix |
|
617 * @param name the name |
|
618 * |
|
619 * Add the QName prefix:name to the hash dict if not present. |
|
620 * |
|
621 * Returns the internal copy of the QName or NULL in case of internal error |
|
622 */ |
|
623 XMLPUBFUNEXPORT const xmlChar * |
|
624 xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) { |
|
625 unsigned long okey, key, nbi = 0; |
|
626 xmlDictEntryPtr entry; |
|
627 xmlDictEntryPtr insert; |
|
628 const xmlChar *ret; |
|
629 int len; |
|
630 |
|
631 if (!dict || !name) |
|
632 return(NULL); |
|
633 |
|
634 len = xmlStrlen(name); |
|
635 if (prefix) |
|
636 len += 1 + xmlStrlen(prefix); |
|
637 |
|
638 /* |
|
639 * Check for duplicate and insertion location. |
|
640 */ |
|
641 okey = xmlDictComputeQKey(prefix, name, len); |
|
642 key = okey % dict->size; |
|
643 if (dict->dict[key].valid == 0) { |
|
644 insert = NULL; |
|
645 } else { |
|
646 for (insert = &(dict->dict[key]); |
|
647 insert->next; |
|
648 insert = insert->next) |
|
649 { |
|
650 if ((insert->len == len) && xmlStrQEqual(prefix, name, insert->name)) |
|
651 return(insert->name); |
|
652 nbi++; |
|
653 } |
|
654 if ((insert->len == len) && xmlStrQEqual(prefix, name, insert->name)) |
|
655 return(insert->name); |
|
656 } |
|
657 |
|
658 if (dict->subdict) |
|
659 { |
|
660 key = okey % dict->subdict->size; |
|
661 if (dict->subdict->dict[key].valid != 0) |
|
662 { |
|
663 xmlDictEntryPtr tmp; |
|
664 for (tmp = &(dict->subdict->dict[key]); |
|
665 tmp->next; |
|
666 tmp = tmp->next) |
|
667 { |
|
668 if ((tmp->len == len) && xmlStrQEqual(prefix, name, tmp->name)) |
|
669 return(tmp->name); |
|
670 nbi++; |
|
671 } |
|
672 if ((tmp->len == len) && xmlStrQEqual(prefix, name, tmp->name)) |
|
673 return(tmp->name); |
|
674 } |
|
675 key = okey % dict->size; |
|
676 } |
|
677 |
|
678 ret = xmlDictAddQString(dict, prefix, name, len); |
|
679 if (!ret) |
|
680 return(NULL); |
|
681 if (!insert) { |
|
682 entry = &(dict->dict[key]); |
|
683 } else { |
|
684 entry = (xmlDictEntryPtr)xmlMalloc(sizeof(xmlDictEntry)); |
|
685 if (!entry) |
|
686 return(NULL); |
|
687 } |
|
688 entry->name = ret; |
|
689 entry->len = len; |
|
690 entry->next = NULL; |
|
691 entry->valid = 1; |
|
692 |
|
693 if (insert) |
|
694 insert->next = entry; |
|
695 |
|
696 dict->nbElems++; |
|
697 |
|
698 if ((nbi > MAX_HASH_LEN) && |
|
699 (dict->size <= ((MAX_DICT_HASH / 2) / MAX_HASH_LEN))) |
|
700 { |
|
701 xmlDictGrow(dict, MAX_HASH_LEN * 2 * dict->size); |
|
702 } |
|
703 /* Note that entry may have been freed at this point by xmlDictGrow */ |
|
704 |
|
705 return(ret); |
|
706 } |
|
707 |
|
708 |
|
709 /** |
|
710 * xmlDictOwns: |
|
711 * @param dict the dictionnary |
|
712 * @param str the string |
|
713 * |
|
714 * check if a string is owned by the disctionary |
|
715 * |
|
716 * Returns 1 if true, 0 if false and -1 in case of error |
|
717 * -1 in case of error |
|
718 * |
|
719 * OOM: impossible |
|
720 */ |
|
721 XMLPUBFUNEXPORT int |
|
722 xmlDictOwns(xmlDictPtr dict, const xmlChar *str) { |
|
723 xmlDictStringsPtr pool; |
|
724 |
|
725 if (!dict || !str) |
|
726 { |
|
727 return(-1); |
|
728 } |
|
729 |
|
730 pool = dict->strings; |
|
731 while (pool) { |
|
732 if ((str >= pool->array) && (str <= pool->free)) |
|
733 { |
|
734 return(1); |
|
735 } |
|
736 pool = pool->next; |
|
737 } |
|
738 |
|
739 if (dict->subdict) |
|
740 return(xmlDictOwns(dict->subdict, str)); |
|
741 return(0); |
|
742 } |
|
743 |
|
744 #ifndef XMLENGINE_EXCLUDE_UNUSED |
|
745 /** |
|
746 * xmlDictSize: |
|
747 * @param dict the dictionnary |
|
748 * |
|
749 * Query the number of elements installed in the hash dict. |
|
750 * |
|
751 * Returns the number of elements in the dictionnary or |
|
752 * -1 in case of error |
|
753 */ |
|
754 int |
|
755 xmlDictSize(xmlDictPtr dict) |
|
756 { |
|
757 if (!dict) |
|
758 return(-1); |
|
759 if (dict->subdict) |
|
760 return(dict->nbElems + dict->subdict->nbElems); |
|
761 return(dict->nbElems); |
|
762 } |
|
763 #endif /* ifndef XMLENGINE_EXCLUDE_UNUSED */ |