1#include <string.h>
2#include <libxml/parser.h>
3#include <libxml/dict.h>
4
5/* #define WITH_PRINT */
6
7static const char *seeds1[] = {
8   "a", "b", "c",
9   "d", "e", "f",
10   "g", "h", "i",
11   "j", "k", "l",
12
13   NULL
14};
15
16static const char *seeds2[] = {
17   "m", "n", "o",
18   "p", "q", "r",
19   "s", "t", "u",
20   "v", "w", "x",
21
22   NULL
23};
24
25#define NB_STRINGS_NS 100
26#define NB_STRINGS_MAX 10000
27#define NB_STRINGS_MIN 10
28
29static xmlChar *strings1[NB_STRINGS_MAX];
30static xmlChar *strings2[NB_STRINGS_MAX];
31static const xmlChar *test1[NB_STRINGS_MAX];
32static const xmlChar *test2[NB_STRINGS_MAX];
33static int nbErrors = 0;
34
35static void fill_strings(void) {
36    int i, j, k;
37
38    /*
39     * That's a bit nasty but the output is fine and it doesn't take hours
40     * there is a small but sufficient number of duplicates, and we have
41     * ":xxx" and full QNames in the last NB_STRINGS_NS values
42     */
43    for (i = 0; seeds1[i] != NULL; i++) {
44        strings1[i] = xmlStrdup((const xmlChar *) seeds1[i]);
45	if (strings1[i] == NULL) {
46	    fprintf(stderr, "Out of memory while generating strings1\n");
47	    exit(1);
48	}
49    }
50    for (j = 0, k = 0;i < NB_STRINGS_MAX - NB_STRINGS_NS;i++,j++) {
51        strings1[i] = xmlStrncatNew(strings1[j], strings1[k], -1);
52	if (strings1[i] == NULL) {
53	    fprintf(stderr, "Out of memory while generating strings1\n");
54	    exit(1);
55	}
56	if (j >= 50) {
57	    j = 0;
58	    k++;
59	}
60    }
61    for (j = 0; (j < 50) && (i < NB_STRINGS_MAX); i++, j+=2) {
62        strings1[i] = xmlStrncatNew(strings1[j], (const xmlChar *) ":", -1);
63	if (strings1[i] == NULL) {
64	    fprintf(stderr, "Out of memory while generating strings1\n");
65	    exit(1);
66	}
67    }
68    for (j = NB_STRINGS_MAX - NB_STRINGS_NS, k = 0;
69         i < NB_STRINGS_MAX;i++,j++) {
70        strings1[i] = xmlStrncatNew(strings1[j], strings1[k], -1);
71	if (strings1[i] == NULL) {
72	    fprintf(stderr, "Out of memory while generating strings1\n");
73	    exit(1);
74	}
75	k += 3;
76	if (k >= 50) k = 0;
77    }
78
79    /*
80     * Now do the same with the second pool of strings
81     */
82    for (i = 0; seeds2[i] != NULL; i++) {
83        strings2[i] = xmlStrdup((const xmlChar *) seeds2[i]);
84	if (strings2[i] == NULL) {
85	    fprintf(stderr, "Out of memory while generating strings2\n");
86	    exit(1);
87	}
88    }
89    for (j = 0, k = 0;i < NB_STRINGS_MAX - NB_STRINGS_NS;i++,j++) {
90        strings2[i] = xmlStrncatNew(strings2[j], strings2[k], -1);
91	if (strings2[i] == NULL) {
92	    fprintf(stderr, "Out of memory while generating strings2\n");
93	    exit(1);
94	}
95	if (j >= 50) {
96	    j = 0;
97	    k++;
98	}
99    }
100    for (j = 0; (j < 50) && (i < NB_STRINGS_MAX); i++, j+=2) {
101        strings2[i] = xmlStrncatNew(strings2[j], (const xmlChar *) ":", -1);
102	if (strings2[i] == NULL) {
103	    fprintf(stderr, "Out of memory while generating strings2\n");
104	    exit(1);
105	}
106    }
107    for (j = NB_STRINGS_MAX - NB_STRINGS_NS, k = 0;
108         i < NB_STRINGS_MAX;i++,j++) {
109        strings2[i] = xmlStrncatNew(strings2[j], strings2[k], -1);
110	if (strings2[i] == NULL) {
111	    fprintf(stderr, "Out of memory while generating strings2\n");
112	    exit(1);
113	}
114	k += 3;
115	if (k >= 50) k = 0;
116    }
117
118}
119
120#ifdef WITH_PRINT
121static void print_strings(void) {
122    int i;
123
124    for (i = 0; i < NB_STRINGS_MAX;i++) {
125        printf("%s\n", strings1[i]);
126    }
127    for (i = 0; i < NB_STRINGS_MAX;i++) {
128        printf("%s\n", strings2[i]);
129    }
130}
131#endif
132
133static void clean_strings(void) {
134    int i;
135
136    for (i = 0; i < NB_STRINGS_MAX; i++) {
137        if (strings1[i] != NULL) /* really should not happen */
138	    xmlFree(strings1[i]);
139    }
140    for (i = 0; i < NB_STRINGS_MAX; i++) {
141        if (strings2[i] != NULL) /* really should not happen */
142	    xmlFree(strings2[i]);
143    }
144}
145
146/*
147 * This tests the sub-dictionary support
148 */
149static int run_test2(xmlDictPtr parent) {
150    int i, j;
151    xmlDictPtr dict;
152    int ret = 0;
153    xmlChar prefix[40];
154    xmlChar *cur, *pref;
155    const xmlChar *tmp;
156
157    dict = xmlDictCreateSub(parent);
158    if (dict == NULL) {
159	fprintf(stderr, "Out of memory while creating sub-dictionary\n");
160	exit(1);
161    }
162    memset(test2, 0, sizeof(test2));
163
164    /*
165     * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow
166     * and we allocate all those doing the fast key computations
167     * All the strings are based on a different seeds subset so we know
168     * they are allocated in the main dictionary, not coming from the parent
169     */
170    for (i = 0;i < NB_STRINGS_MIN;i++) {
171        test2[i] = xmlDictLookup(dict, strings2[i], -1);
172	if (test2[i] == NULL) {
173	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]);
174	    ret = 1;
175	    nbErrors++;
176	}
177    }
178    j = NB_STRINGS_MAX - NB_STRINGS_NS;
179    /* ":foo" like strings2 */
180    for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
181        test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j]));
182	if (test2[j] == NULL) {
183	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]);
184	    ret = 1;
185	    nbErrors++;
186	}
187    }
188    /* "a:foo" like strings2 */
189    j = NB_STRINGS_MAX - NB_STRINGS_MIN;
190    for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
191        test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j]));
192	if (test2[j] == NULL) {
193	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]);
194	    ret = 1;
195	    nbErrors++;
196	}
197    }
198
199    /*
200     * At this point allocate all the strings
201     * the dictionary will grow in the process, reallocate more string tables
202     * and switch to the better key generator
203     */
204    for (i = 0;i < NB_STRINGS_MAX;i++) {
205        if (test2[i] != NULL)
206	    continue;
207	test2[i] = xmlDictLookup(dict, strings2[i], -1);
208	if (test2[i] == NULL) {
209	    fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]);
210	    ret = 1;
211	    nbErrors++;
212	}
213    }
214
215    /*
216     * Now we can start to test things, first that all strings2 belongs to
217     * the dict, and that none of them was actually allocated in the parent
218     */
219    for (i = 0;i < NB_STRINGS_MAX;i++) {
220        if (!xmlDictOwns(dict, test2[i])) {
221	    fprintf(stderr, "Failed ownership failure for '%s'\n",
222	            strings2[i]);
223	    ret = 1;
224	    nbErrors++;
225	}
226        if (xmlDictOwns(parent, test2[i])) {
227	    fprintf(stderr, "Failed parent ownership failure for '%s'\n",
228	            strings2[i]);
229	    ret = 1;
230	    nbErrors++;
231	}
232    }
233
234    /*
235     * Also verify that all strings from the parent are seen from the subdict
236     */
237    for (i = 0;i < NB_STRINGS_MAX;i++) {
238        if (!xmlDictOwns(dict, test1[i])) {
239	    fprintf(stderr, "Failed sub-ownership failure for '%s'\n",
240	            strings1[i]);
241	    ret = 1;
242	    nbErrors++;
243	}
244    }
245
246    /*
247     * Then that another lookup to the string in sub will return the same
248     */
249    for (i = 0;i < NB_STRINGS_MAX;i++) {
250        if (xmlDictLookup(dict, strings2[i], -1) != test2[i]) {
251	    fprintf(stderr, "Failed re-lookup check for %d, '%s'\n",
252	            i, strings2[i]);
253	    ret = 1;
254	    nbErrors++;
255	}
256    }
257    /*
258     * But also that any lookup for a string in the parent will be provided
259     * as in the parent
260     */
261    for (i = 0;i < NB_STRINGS_MAX;i++) {
262        if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) {
263	    fprintf(stderr, "Failed parent string lookup check for %d, '%s'\n",
264	            i, strings1[i]);
265	    ret = 1;
266	    nbErrors++;
267	}
268    }
269
270    /*
271     * check the QName lookups
272     */
273    for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
274        cur = strings2[i];
275	pref = &prefix[0];
276	while (*cur != ':') *pref++ = *cur++;
277	cur++;
278	*pref = 0;
279	tmp = xmlDictQLookup(dict, &prefix[0], cur);
280	if (xmlDictQLookup(dict, &prefix[0], cur) != test2[i]) {
281	    fprintf(stderr, "Failed lookup check for '%s':'%s'\n",
282	            &prefix[0], cur);
283            ret = 1;
284	    nbErrors++;
285	}
286    }
287    /*
288     * check the QName lookups for strings from the parent
289     */
290    for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
291        cur = strings1[i];
292	pref = &prefix[0];
293	while (*cur != ':') *pref++ = *cur++;
294	cur++;
295	*pref = 0;
296	tmp = xmlDictQLookup(dict, &prefix[0], cur);
297	if (xmlDictQLookup(dict, &prefix[0], cur) != test1[i]) {
298	    fprintf(stderr, "Failed parent lookup check for '%s':'%s'\n",
299	            &prefix[0], cur);
300            ret = 1;
301	    nbErrors++;
302	}
303    }
304
305    xmlDictFree(dict);
306    return(ret);
307}
308
309/*
310 * Test a single dictionary
311 */
312static int run_test1(void) {
313    int i, j;
314    xmlDictPtr dict;
315    int ret = 0;
316    xmlChar prefix[40];
317    xmlChar *cur, *pref;
318    const xmlChar *tmp;
319
320    dict = xmlDictCreate();
321    if (dict == NULL) {
322	fprintf(stderr, "Out of memory while creating dictionary\n");
323	exit(1);
324    }
325    memset(test1, 0, sizeof(test1));
326
327    /*
328     * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow
329     * and we allocate all those doing the fast key computations
330     */
331    for (i = 0;i < NB_STRINGS_MIN;i++) {
332        test1[i] = xmlDictLookup(dict, strings1[i], -1);
333	if (test1[i] == NULL) {
334	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]);
335	    ret = 1;
336	    nbErrors++;
337	}
338    }
339    j = NB_STRINGS_MAX - NB_STRINGS_NS;
340    /* ":foo" like strings1 */
341    for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
342        test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j]));
343	if (test1[j] == NULL) {
344	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]);
345	    ret = 1;
346	    nbErrors++;
347	}
348    }
349    /* "a:foo" like strings1 */
350    j = NB_STRINGS_MAX - NB_STRINGS_MIN;
351    for (i = 0;i < NB_STRINGS_MIN;i++, j++) {
352        test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j]));
353	if (test1[j] == NULL) {
354	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]);
355	    ret = 1;
356	    nbErrors++;
357	}
358    }
359
360    /*
361     * At this point allocate all the strings
362     * the dictionary will grow in the process, reallocate more string tables
363     * and switch to the better key generator
364     */
365    for (i = 0;i < NB_STRINGS_MAX;i++) {
366        if (test1[i] != NULL)
367	    continue;
368	test1[i] = xmlDictLookup(dict, strings1[i], -1);
369	if (test1[i] == NULL) {
370	    fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]);
371	    ret = 1;
372	    nbErrors++;
373	}
374    }
375
376    /*
377     * Now we can start to test things, first that all strings1 belongs to
378     * the dict
379     */
380    for (i = 0;i < NB_STRINGS_MAX;i++) {
381        if (!xmlDictOwns(dict, test1[i])) {
382	    fprintf(stderr, "Failed ownership failure for '%s'\n",
383	            strings1[i]);
384	    ret = 1;
385	    nbErrors++;
386	}
387    }
388
389    /*
390     * Then that another lookup to the string will return the same
391     */
392    for (i = 0;i < NB_STRINGS_MAX;i++) {
393        if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) {
394	    fprintf(stderr, "Failed re-lookup check for %d, '%s'\n",
395	            i, strings1[i]);
396	    ret = 1;
397	    nbErrors++;
398	}
399    }
400
401    /*
402     * More complex, check the QName lookups
403     */
404    for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) {
405        cur = strings1[i];
406	pref = &prefix[0];
407	while (*cur != ':') *pref++ = *cur++;
408	cur++;
409	*pref = 0;
410	tmp = xmlDictQLookup(dict, &prefix[0], cur);
411	if (xmlDictQLookup(dict, &prefix[0], cur) != test1[i]) {
412	    fprintf(stderr, "Failed lookup check for '%s':'%s'\n",
413	            &prefix[0], cur);
414            ret = 1;
415	    nbErrors++;
416	}
417    }
418
419    run_test2(dict);
420
421    xmlDictFree(dict);
422    return(ret);
423}
424
425int main(void)
426{
427    int ret;
428
429    LIBXML_TEST_VERSION
430    fill_strings();
431#ifdef WITH_PRINT
432    print_strings();
433#endif
434    ret = run_test1();
435    if (ret == 0) {
436        printf("dictionary tests succeeded %d strings\n", 2 * NB_STRINGS_MAX);
437    } else {
438        printf("dictionary tests failed with %d errors\n", nbErrors);
439    }
440    clean_strings();
441    xmlCleanupParser();
442    xmlMemoryDump();
443    return(ret);
444}
445