• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdlib.h>
2 #include <string.h>
3 #include <libxml/parser.h>
4 #include <libxml/dict.h>
5 
6 
7 /**** dictionary tests ****/
8 
9 /* #define WITH_PRINT */
10 
11 static const char *seeds1[] = {
12    "a", "b", "c",
13    "d", "e", "f",
14    "g", "h", "i",
15    "j", "k", "l",
16 
17    NULL
18 };
19 
20 static const char *seeds2[] = {
21    "m", "n", "o",
22    "p", "q", "r",
23    "s", "t", "u",
24    "v", "w", "x",
25 
26    NULL
27 };
28 
29 #define NB_STRINGS_MAX 100000
30 #define NB_STRINGS_NS  10000
31 #define NB_STRINGS_PREFIX (NB_STRINGS_NS / 20)
32 #define NB_STRINGS_MIN 10
33 
34 static xmlChar **strings1;
35 static xmlChar **strings2;
36 static const xmlChar **test1;
37 static const xmlChar **test2;
38 static int nbErrors = 0;
39 
40 static void
fill_string_pool(xmlChar ** strings,const char ** seeds)41 fill_string_pool(xmlChar **strings, const char **seeds) {
42     int i, j, k;
43     int start_ns = NB_STRINGS_MAX - NB_STRINGS_NS;
44 
45     /*
46      * That's a bit nasty but the output is fine and it doesn't take hours
47      * there is a small but sufficient number of duplicates, and we have
48      * ":xxx" and full QNames in the last NB_STRINGS_NS values
49      */
50     for (i = 0; seeds[i] != NULL; i++) {
51         strings[i] = xmlStrdup((const xmlChar *) seeds[i]);
52 	if (strings[i] == NULL) {
53 	    fprintf(stderr, "Out of memory while generating strings\n");
54 	    exit(1);
55 	}
56     }
57     for (j = 0, k = 0; i < start_ns; i++) {
58         strings[i] = xmlStrncatNew(strings[j], strings[k], -1);
59 	if (strings[i] == NULL) {
60 	    fprintf(stderr, "Out of memory while generating strings\n");
61 	    exit(1);
62 	}
63         if (xmlStrlen(strings[i]) > 30) {
64             fprintf(stderr, "### %s %s\n", strings[start_ns+j], strings[k]);
65             abort();
66         }
67         j++;
68 	if (j >= 50) {
69 	    j = 0;
70 	    k++;
71 	}
72     }
73     for (j = 0, k = 0; (j < NB_STRINGS_PREFIX) && (i < NB_STRINGS_MAX);
74          i++, j++) {
75         strings[i] = xmlStrncatNew(strings[k], (const xmlChar *) ":", -1);
76 	if (strings[i] == NULL) {
77 	    fprintf(stderr, "Out of memory while generating strings\n");
78 	    exit(1);
79 	}
80         k += 1;
81         if (k >= start_ns) k = 0;
82     }
83     for (j = 0, k = 0; i < NB_STRINGS_MAX; i++) {
84         strings[i] = xmlStrncatNew(strings[start_ns+j], strings[k], -1);
85 	if (strings[i] == NULL) {
86 	    fprintf(stderr, "Out of memory while generating strings\n");
87 	    exit(1);
88 	}
89         j++;
90         if (j >= NB_STRINGS_PREFIX) j = 0;
91 	k += 5;
92         if (k >= start_ns) k = 0;
93     }
94 }
95 
96 #ifdef WITH_PRINT
print_strings(void)97 static void print_strings(void) {
98     int i;
99 
100     for (i = 0; i < NB_STRINGS_MAX;i++) {
101         printf("%s\n", strings1[i]);
102     }
103     for (i = 0; i < NB_STRINGS_MAX;i++) {
104         printf("%s\n", strings2[i]);
105     }
106 }
107 #endif
108 
clean_strings(void)109 static void clean_strings(void) {
110     int i;
111 
112     for (i = 0; i < NB_STRINGS_MAX; i++) {
113         if (strings1[i] != NULL) /* really should not happen */
114 	    xmlFree(strings1[i]);
115     }
116     for (i = 0; i < NB_STRINGS_MAX; i++) {
117         if (strings2[i] != NULL) /* really should not happen */
118 	    xmlFree(strings2[i]);
119     }
120 }
121 
122 /*
123  * This tests the sub-dictionary support
124  */
125 static int
test_subdict(xmlDictPtr parent)126 test_subdict(xmlDictPtr parent) {
127     int i, j;
128     xmlDictPtr dict;
129     int ret = 0;
130     xmlChar prefix[40];
131     xmlChar *cur, *pref;
132     const xmlChar *tmp;
133 
134     dict = xmlDictCreateSub(parent);
135     if (dict == NULL) {
136 	fprintf(stderr, "Out of memory while creating sub-dictionary\n");
137 	exit(1);
138     }
139     /* Cast to avoid buggy warning on MSVC. */
140     memset((void *) test2, 0, sizeof(test2));
141 
142     /*
143      * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow
144      * and we allocate all those doing the fast key computations
145      * All the strings are based on a different seeds subset so we know
146      * they are allocated in the main dictionary, not coming from the parent
147      */
148     for (i = 0;i < NB_STRINGS_MIN;i++) {
149         test2[i] = xmlDictLookup(dict, strings2[i], -1);
150 	if (test2[i] == NULL) {
151 	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]);
152 	    ret = 1;
153 	    nbErrors++;
154 	}
155     }
156     j = NB_STRINGS_MAX - NB_STRINGS_NS;
157     /* ":foo" like strings2 */
158     for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
159         test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j]));
160 	if (test2[j] == NULL) {
161 	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]);
162 	    ret = 1;
163 	    nbErrors++;
164 	}
165     }
166     /* "a:foo" like strings2 */
167     j = NB_STRINGS_MAX - NB_STRINGS_MIN;
168     for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
169         test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j]));
170 	if (test2[j] == NULL) {
171 	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]);
172 	    ret = 1;
173 	    nbErrors++;
174 	}
175     }
176 
177     /*
178      * At this point allocate all the strings
179      * the dictionary will grow in the process, reallocate more string tables
180      * and switch to the better key generator
181      */
182     for (i = 0;i < NB_STRINGS_MAX;i++) {
183         if (test2[i] != NULL)
184 	    continue;
185 	test2[i] = xmlDictLookup(dict, strings2[i], -1);
186 	if (test2[i] == NULL) {
187 	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]);
188 	    ret = 1;
189 	    nbErrors++;
190 	}
191     }
192 
193     /*
194      * Now we can start to test things, first that all strings2 belongs to
195      * the dict, and that none of them was actually allocated in the parent
196      */
197     for (i = 0;i < NB_STRINGS_MAX;i++) {
198         if (!xmlDictOwns(dict, test2[i])) {
199 	    fprintf(stderr, "Failed ownership failure for '%s'\n",
200 	            strings2[i]);
201 	    ret = 1;
202 	    nbErrors++;
203 	}
204         if (xmlDictOwns(parent, test2[i])) {
205 	    fprintf(stderr, "Failed parent ownership failure for '%s'\n",
206 	            strings2[i]);
207 	    ret = 1;
208 	    nbErrors++;
209 	}
210     }
211 
212     /*
213      * Also verify that all strings from the parent are seen from the subdict
214      */
215     for (i = 0;i < NB_STRINGS_MAX;i++) {
216         if (!xmlDictOwns(dict, test1[i])) {
217 	    fprintf(stderr, "Failed sub-ownership failure for '%s'\n",
218 	            strings1[i]);
219 	    ret = 1;
220 	    nbErrors++;
221 	}
222     }
223 
224     /*
225      * Then that another lookup to the string in sub will return the same
226      */
227     for (i = 0;i < NB_STRINGS_MAX;i++) {
228         if (xmlDictLookup(dict, strings2[i], -1) != test2[i]) {
229 	    fprintf(stderr, "Failed re-lookup check for %d, '%s'\n",
230 	            i, strings2[i]);
231 	    ret = 1;
232 	    nbErrors++;
233 	}
234     }
235     /*
236      * But also that any lookup for a string in the parent will be provided
237      * as in the parent
238      */
239     for (i = 0;i < NB_STRINGS_MAX;i++) {
240         if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) {
241 	    fprintf(stderr, "Failed parent string lookup check for %d, '%s'\n",
242 	            i, strings1[i]);
243 	    ret = 1;
244 	    nbErrors++;
245 	}
246     }
247 
248     /*
249      * check the QName lookups
250      */
251     for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
252         cur = strings2[i];
253 	pref = &prefix[0];
254 	while (*cur != ':') *pref++ = *cur++;
255 	cur++;
256 	*pref = 0;
257 	tmp = xmlDictQLookup(dict, &prefix[0], cur);
258 	if (tmp != test2[i]) {
259 	    fprintf(stderr, "Failed lookup check for '%s':'%s'\n",
260 	            &prefix[0], cur);
261             ret = 1;
262 	    nbErrors++;
263 	}
264     }
265     /*
266      * check the QName lookups for strings from the parent
267      */
268     for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
269         cur = strings1[i];
270 	pref = &prefix[0];
271 	while (*cur != ':') *pref++ = *cur++;
272 	cur++;
273 	*pref = 0;
274 	tmp = xmlDictQLookup(dict, &prefix[0], cur);
275 	if (xmlDictQLookup(dict, &prefix[0], cur) != test1[i]) {
276 	    fprintf(stderr, "Failed parent lookup check for '%s':'%s'\n",
277 	            &prefix[0], cur);
278             ret = 1;
279 	    nbErrors++;
280 	}
281     }
282 
283     xmlDictFree(dict);
284     return(ret);
285 }
286 
287 /*
288  * Test a single dictionary
289  */
290 static int
test_dict(xmlDict * dict)291 test_dict(xmlDict *dict) {
292     int i, j;
293     int ret = 0;
294     xmlChar prefix[40];
295     xmlChar *cur, *pref;
296     const xmlChar *tmp;
297 
298     /* Cast to avoid buggy warning on MSVC. */
299     memset((void *) test1, 0, sizeof(test1));
300 
301     /*
302      * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow
303      * and we allocate all those doing the fast key computations
304      */
305     for (i = 0;i < NB_STRINGS_MIN;i++) {
306         test1[i] = xmlDictLookup(dict, strings1[i], -1);
307 	if (test1[i] == NULL) {
308 	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]);
309 	    ret = 1;
310 	    nbErrors++;
311 	}
312     }
313     j = NB_STRINGS_MAX - NB_STRINGS_NS;
314     /* ":foo" like strings1 */
315     for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
316         test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j]));
317 	if (test1[j] == NULL) {
318 	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]);
319 	    ret = 1;
320 	    nbErrors++;
321 	}
322     }
323     /* "a:foo" like strings1 */
324     j = NB_STRINGS_MAX - NB_STRINGS_MIN;
325     for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
326         test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j]));
327 	if (test1[j] == NULL) {
328 	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]);
329 	    ret = 1;
330 	    nbErrors++;
331 	}
332     }
333 
334     /*
335      * At this point allocate all the strings
336      * the dictionary will grow in the process, reallocate more string tables
337      * and switch to the better key generator
338      */
339     for (i = 0;i < NB_STRINGS_MAX;i++) {
340         if (test1[i] != NULL)
341 	    continue;
342 	test1[i] = xmlDictLookup(dict, strings1[i], -1);
343 	if (test1[i] == NULL) {
344 	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]);
345 	    ret = 1;
346 	    nbErrors++;
347 	}
348     }
349 
350     /*
351      * Now we can start to test things, first that all strings1 belongs to
352      * the dict
353      */
354     for (i = 0;i < NB_STRINGS_MAX;i++) {
355         if (!xmlDictOwns(dict, test1[i])) {
356 	    fprintf(stderr, "Failed ownership failure for '%s'\n",
357 	            strings1[i]);
358 	    ret = 1;
359 	    nbErrors++;
360 	}
361     }
362 
363     /*
364      * Then that another lookup to the string will return the same
365      */
366     for (i = 0;i < NB_STRINGS_MAX;i++) {
367         if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) {
368 	    fprintf(stderr, "Failed re-lookup check for %d, '%s'\n",
369 	            i, strings1[i]);
370 	    ret = 1;
371 	    nbErrors++;
372 	}
373     }
374 
375     /*
376      * More complex, check the QName lookups
377      */
378     for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
379         cur = strings1[i];
380 	pref = &prefix[0];
381 	while (*cur != ':') *pref++ = *cur++;
382 	cur++;
383 	*pref = 0;
384 	tmp = xmlDictQLookup(dict, &prefix[0], cur);
385 	if (tmp != test1[i]) {
386 	    fprintf(stderr, "Failed lookup check for '%s':'%s'\n",
387 	            &prefix[0], cur);
388             ret = 1;
389 	    nbErrors++;
390 	}
391     }
392 
393     return(ret);
394 }
395 
396 static int
testall_dict(void)397 testall_dict(void) {
398     xmlDictPtr dict;
399     int ret = 0;
400 
401     strings1 = xmlMalloc(NB_STRINGS_MAX * sizeof(strings1[0]));
402     memset(strings1, 0, NB_STRINGS_MAX * sizeof(strings1[0]));
403     strings2 = xmlMalloc(NB_STRINGS_MAX * sizeof(strings2[0]));
404     memset(strings2, 0, NB_STRINGS_MAX * sizeof(strings2[0]));
405     test1 = xmlMalloc(NB_STRINGS_MAX * sizeof(test1[0]));
406     memset(test1, 0, NB_STRINGS_MAX * sizeof(test1[0]));
407     test2 = xmlMalloc(NB_STRINGS_MAX * sizeof(test2[0]));
408     memset(test2, 0, NB_STRINGS_MAX * sizeof(test2[0]));
409 
410     fill_string_pool(strings1, seeds1);
411     fill_string_pool(strings2, seeds2);
412 #ifdef WITH_PRINT
413     print_strings();
414 #endif
415 
416     dict = xmlDictCreate();
417     if (dict == NULL) {
418 	fprintf(stderr, "Out of memory while creating dictionary\n");
419 	exit(1);
420     }
421     if (test_dict(dict) != 0) {
422         ret = 1;
423     }
424     if (test_subdict(dict) != 0) {
425         ret = 1;
426     }
427     xmlDictFree(dict);
428 
429     clean_strings();
430     xmlFree(strings1);
431     xmlFree(strings2);
432     xmlFree(test1);
433     xmlFree(test2);
434 
435     return ret;
436 }
437 
438 
439 /**** Hash table tests ****/
440 
441 static unsigned
442 rng_state[2] = { 123, 456 };
443 
444 #define HASH_ROL(x,n) ((x) << (n) | ((x) & 0xFFFFFFFF) >> (32 - (n)))
445 
446 #ifdef __clang__
447 __attribute__ ((no_sanitize("unsigned-integer-overflow")))
448 __attribute__ ((no_sanitize("unsigned-shift-base")))
449 #endif
450 static unsigned
my_rand(unsigned max)451 my_rand(unsigned max) {
452     unsigned s0 = rng_state[0];
453     unsigned s1 = rng_state[1];
454     unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5;
455 
456     s1 ^= s0;
457     rng_state[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9);
458     rng_state[1] = HASH_ROL(s1, 13);
459 
460     return((result & 0xFFFFFFFF) % max);
461 }
462 
463 static xmlChar *
gen_random_string(xmlChar id)464 gen_random_string(xmlChar id) {
465     unsigned size = my_rand(64) + 1;
466     unsigned id_pos = my_rand(size);
467     size_t j;
468 
469     xmlChar *str = xmlMalloc(size + 1);
470     for (j = 0; j < size; j++) {
471         str[j] = 'a' + my_rand(26);
472     }
473     str[id_pos] = id;
474     str[size] = 0;
475 
476     /* Generate QName in 75% of cases */
477     if (size > 3 && my_rand(4) > 0) {
478         unsigned colon_pos = my_rand(size - 3) + 1;
479 
480         if (colon_pos >= id_pos)
481             colon_pos++;
482         str[colon_pos] = ':';
483     }
484 
485     return str;
486 }
487 
488 typedef struct {
489     xmlChar **strings;
490     size_t num_entries;
491     size_t num_keys;
492     size_t num_strings;
493     size_t index;
494     xmlChar id;
495 } StringPool;
496 
497 static StringPool *
pool_new(size_t num_entries,size_t num_keys,xmlChar id)498 pool_new(size_t num_entries, size_t num_keys, xmlChar id) {
499     StringPool *ret;
500     size_t num_strings;
501 
502     ret = xmlMalloc(sizeof(*ret));
503     ret->num_entries = num_entries;
504     ret->num_keys = num_keys;
505     num_strings = num_entries * num_keys;
506     ret->strings = xmlMalloc(num_strings * sizeof(ret->strings[0]));
507     memset(ret->strings, 0, num_strings * sizeof(ret->strings[0]));
508     ret->num_strings = num_strings;
509     ret->index = 0;
510     ret->id = id;
511 
512     return ret;
513 }
514 
515 static void
pool_free(StringPool * pool)516 pool_free(StringPool *pool) {
517     size_t i;
518 
519     for (i = 0; i < pool->num_strings; i++) {
520         xmlFree(pool->strings[i]);
521     }
522     xmlFree(pool->strings);
523     xmlFree(pool);
524 }
525 
526 static int
pool_done(StringPool * pool)527 pool_done(StringPool *pool) {
528     return pool->index >= pool->num_strings;
529 }
530 
531 static void
pool_reset(StringPool * pool)532 pool_reset(StringPool *pool) {
533     pool->index = 0;
534 }
535 
536 static int
pool_bulk_insert(StringPool * pool,xmlHashTablePtr hash,size_t num)537 pool_bulk_insert(StringPool *pool, xmlHashTablePtr hash, size_t num) {
538     size_t i, j;
539     int ret = 0;
540 
541     for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) {
542         xmlChar *str[3];
543         size_t k;
544 
545         while (1) {
546             xmlChar tmp_key[1];
547             int res;
548 
549             for (k = 0; k < pool->num_keys; k++)
550                 str[k] = gen_random_string(pool->id);
551 
552             switch (pool->num_keys) {
553                 case 1:
554                     res = xmlHashAddEntry(hash, str[0], tmp_key);
555                     if (res == 0 &&
556                         xmlHashUpdateEntry(hash, str[0], str[0], NULL) != 0)
557                         ret = -1;
558                     break;
559                 case 2:
560                     res = xmlHashAddEntry2(hash, str[0], str[1], tmp_key);
561                     if (res == 0 &&
562                         xmlHashUpdateEntry2(hash, str[0], str[1], str[0],
563                                             NULL) != 0)
564                         ret = -1;
565                     break;
566                 case 3:
567                     res = xmlHashAddEntry3(hash, str[0], str[1], str[2],
568                                            tmp_key);
569                     if (res == 0 &&
570                         xmlHashUpdateEntry3(hash, str[0], str[1], str[2],
571                                             str[0], NULL) != 0)
572                         ret = -1;
573                     break;
574             }
575 
576             if (res == 0)
577                 break;
578             for (k = 0; k < pool->num_keys; k++)
579                 xmlFree(str[k]);
580         }
581 
582         for (k = 0; k < pool->num_keys; k++)
583             pool->strings[i++] = str[k];
584     }
585 
586     pool->index = i;
587     return ret;
588 }
589 
590 static xmlChar *
hash_qlookup(xmlHashTable * hash,xmlChar ** names,size_t num_keys)591 hash_qlookup(xmlHashTable *hash, xmlChar **names, size_t num_keys) {
592     xmlChar *prefix[3];
593     const xmlChar *local[3];
594     xmlChar *res;
595     size_t i;
596 
597     for (i = 0; i < 3; ++i) {
598         if (i >= num_keys) {
599             prefix[i] = NULL;
600             local[i] = NULL;
601         } else {
602             const xmlChar *name = names[i];
603             const xmlChar *colon = BAD_CAST strchr((const char *) name, ':');
604 
605             if (colon == NULL) {
606                 prefix[i] = NULL;
607                 local[i] = name;
608             } else {
609                 prefix[i] = xmlStrndup(name, colon - name);
610                 local[i] = &colon[1];
611             }
612         }
613     }
614 
615     res = xmlHashQLookup3(hash, prefix[0], local[0], prefix[1], local[1],
616                           prefix[2], local[2]);
617 
618     for (i = 0; i < 3; ++i)
619         xmlFree(prefix[i]);
620 
621     return res;
622 }
623 
624 static int
pool_bulk_lookup(StringPool * pool,xmlHashTablePtr hash,size_t num,int existing)625 pool_bulk_lookup(StringPool *pool, xmlHashTablePtr hash, size_t num,
626                  int existing) {
627     size_t i, j;
628     int ret = 0;
629 
630     for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) {
631         xmlChar **str = &pool->strings[i];
632         int q;
633 
634         for (q = 0; q < 2; q++) {
635             xmlChar *res = NULL;
636 
637             if (q) {
638                 res = hash_qlookup(hash, str, pool->num_keys);
639             } else {
640                 switch (pool->num_keys) {
641                     case 1:
642                         res = xmlHashLookup(hash, str[0]);
643                         break;
644                     case 2:
645                         res = xmlHashLookup2(hash, str[0], str[1]);
646                         break;
647                     case 3:
648                         res = xmlHashLookup3(hash, str[0], str[1], str[2]);
649                         break;
650                 }
651             }
652 
653             if (existing) {
654                 if (res != str[0])
655                     ret = -1;
656             } else {
657                 if (res != NULL)
658                     ret = -1;
659             }
660         }
661 
662         i += pool->num_keys;
663     }
664 
665     pool->index = i;
666     return ret;
667 }
668 
669 static int
pool_bulk_remove(StringPool * pool,xmlHashTablePtr hash,size_t num)670 pool_bulk_remove(StringPool *pool, xmlHashTablePtr hash, size_t num) {
671     size_t i, j;
672     int ret = 0;
673 
674     for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) {
675         xmlChar **str = &pool->strings[i];
676         int res = -1;
677 
678         switch (pool->num_keys) {
679             case 1:
680                 res = xmlHashRemoveEntry(hash, str[0], NULL);
681                 break;
682             case 2:
683                 res = xmlHashRemoveEntry2(hash, str[0], str[1], NULL);
684                 break;
685             case 3:
686                 res = xmlHashRemoveEntry3(hash, str[0], str[1], str[2], NULL);
687                 break;
688         }
689 
690         if (res != 0)
691             ret = -1;
692 
693         i += pool->num_keys;
694     }
695 
696     pool->index = i;
697     return ret;
698 }
699 
700 static int
test_hash(size_t num_entries,size_t num_keys,int use_dict)701 test_hash(size_t num_entries, size_t num_keys, int use_dict) {
702     xmlDict *dict = NULL;
703     xmlHashTable *hash;
704     StringPool *pool1, *pool2;
705     int ret = 0;
706 
707     if (use_dict) {
708         dict = xmlDictCreate();
709         hash = xmlHashCreateDict(0, dict);
710     } else {
711         hash = xmlHashCreate(0);
712     }
713     pool1 = pool_new(num_entries, num_keys, '1');
714     pool2 = pool_new(num_entries, num_keys, '2');
715 
716     /* Insert all strings from pool2 and about half of pool1. */
717     while (!pool_done(pool2)) {
718         if (pool_bulk_insert(pool1, hash, my_rand(50)) != 0) {
719             fprintf(stderr, "pool1: hash insert failed\n");
720             ret = 1;
721         }
722         if (pool_bulk_insert(pool2, hash, my_rand(100)) != 0) {
723             fprintf(stderr, "pool1: hash insert failed\n");
724             ret = 1;
725         }
726     }
727 
728     /* Check existing entries */
729     pool_reset(pool2);
730     if (pool_bulk_lookup(pool2, hash, pool2->num_entries, 1) != 0) {
731         fprintf(stderr, "pool2: hash lookup failed\n");
732         ret = 1;
733     }
734 
735     /* Remove all strings from pool2 and insert the rest of pool1. */
736     pool_reset(pool2);
737     while (!pool_done(pool1) || !pool_done(pool2)) {
738         if (pool_bulk_insert(pool1, hash, my_rand(50)) != 0) {
739             fprintf(stderr, "pool1: hash insert failed\n");
740             ret = 1;
741         }
742         if (pool_bulk_remove(pool2, hash, my_rand(100)) != 0) {
743             fprintf(stderr, "pool2: hash remove failed\n");
744             ret = 1;
745         }
746     }
747 
748     /* Check existing entries */
749     pool_reset(pool1);
750     if (pool_bulk_lookup(pool1, hash, pool1->num_entries, 1) != 0) {
751         fprintf(stderr, "pool1: hash lookup failed\n");
752         ret = 1;
753     }
754 
755     /* Check removed entries */
756     pool_reset(pool2);
757     if (pool_bulk_lookup(pool2, hash, pool2->num_entries, 0) != 0) {
758         fprintf(stderr, "pool2: hash lookup succeeded unexpectedly\n");
759         ret = 1;
760     }
761 
762     pool_free(pool1);
763     pool_free(pool2);
764     xmlHashFree(hash, NULL);
765     xmlDictFree(dict);
766 
767     return ret;
768 }
769 
770 static int
testall_hash(void)771 testall_hash(void) {
772     size_t num_keys;
773 
774     for (num_keys = 1; num_keys <= 3; num_keys++) {
775         size_t num_strings;
776         size_t max_strings = num_keys == 1 ? 100000 : 1000;
777 
778         for (num_strings = 10; num_strings <= max_strings; num_strings *= 10) {
779             size_t reps, i;
780 
781             reps = 1000 / num_strings;
782             if (reps == 0)
783                 reps = 1;
784 
785             for (i = 0; i < reps; i++) {
786                 if (test_hash(num_strings, num_keys, /* use_dict */ 0) != 0)
787                     return(1);
788             }
789 
790             if (test_hash(num_strings, num_keys, /* use_dict */ 1) != 0)
791                 return(1);
792         }
793     }
794 
795     return(0);
796 }
797 
798 
799 /**** main ****/
800 
801 int
main(void)802 main(void) {
803     int ret = 0;
804 
805     LIBXML_TEST_VERSION
806 
807     if (testall_dict() != 0) {
808         fprintf(stderr, "dictionary tests failed\n");
809         ret = 1;
810     }
811     if (testall_hash() != 0) {
812         fprintf(stderr, "hash tests failed\n");
813         ret = 1;
814     }
815 
816     xmlCleanupParser();
817     return(ret);
818 }
819