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