1 #include <string.h>
2 #include <libxml/parser.h>
3 #include <libxml/dict.h>
4 
5 /* #define WITH_PRINT */
6 
7 static const char *seeds1[] = {
8    "a", "b", "c",
9    "d", "e", "f",
10    "g", "h", "i",
11    "j", "k", "l",
12 
13    NULL
14 };
15 
16 static 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 
29 static xmlChar *strings1[NB_STRINGS_MAX];
30 static xmlChar *strings2[NB_STRINGS_MAX];
31 static const xmlChar *test1[NB_STRINGS_MAX];
32 static const xmlChar *test2[NB_STRINGS_MAX];
33 static int nbErrors = 0;
34 
fill_strings(void)35 static 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
print_strings(void)121 static 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 
clean_strings(void)133 static 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  */
run_test2(xmlDictPtr parent)149 static 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 (tmp != 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  */
run_test1(void)312 static 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 (tmp != 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 
main(void)425 int 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