| #include <string.h> | |
| #include <libxml/parser.h> | |
| #include <libxml/dict.h> | |
| /* #define WITH_PRINT */ | |
| static const char *seeds1[] = { | |
| "a", "b", "c", | |
| "d", "e", "f", | |
| "g", "h", "i", | |
| "j", "k", "l", | |
| NULL | |
| }; | |
| static const char *seeds2[] = { | |
| "m", "n", "o", | |
| "p", "q", "r", | |
| "s", "t", "u", | |
| "v", "w", "x", | |
| NULL | |
| }; | |
| #define NB_STRINGS_NS 100 | |
| #define NB_STRINGS_MAX 10000 | |
| #define NB_STRINGS_MIN 10 | |
| static xmlChar *strings1[NB_STRINGS_MAX]; | |
| static xmlChar *strings2[NB_STRINGS_MAX]; | |
| static const xmlChar *test1[NB_STRINGS_MAX]; | |
| static const xmlChar *test2[NB_STRINGS_MAX]; | |
| static int nbErrors = 0; | |
| static void fill_strings(void) { | |
| int i, j, k; | |
| /* | |
| * That's a bit nasty but the output is fine and it doesn't take hours | |
| * there is a small but sufficient number of duplicates, and we have | |
| * ":xxx" and full QNames in the last NB_STRINGS_NS values | |
| */ | |
| for (i = 0; seeds1[i] != NULL; i++) { | |
| strings1[i] = xmlStrdup((const xmlChar *) seeds1[i]); | |
| if (strings1[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings1\n"); | |
| exit(1); | |
| } | |
| } | |
| for (j = 0, k = 0;i < NB_STRINGS_MAX - NB_STRINGS_NS;i++,j++) { | |
| strings1[i] = xmlStrncatNew(strings1[j], strings1[k], -1); | |
| if (strings1[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings1\n"); | |
| exit(1); | |
| } | |
| if (j >= 50) { | |
| j = 0; | |
| k++; | |
| } | |
| } | |
| for (j = 0; (j < 50) && (i < NB_STRINGS_MAX); i++, j+=2) { | |
| strings1[i] = xmlStrncatNew(strings1[j], (const xmlChar *) ":", -1); | |
| if (strings1[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings1\n"); | |
| exit(1); | |
| } | |
| } | |
| for (j = NB_STRINGS_MAX - NB_STRINGS_NS, k = 0; | |
| i < NB_STRINGS_MAX;i++,j++) { | |
| strings1[i] = xmlStrncatNew(strings1[j], strings1[k], -1); | |
| if (strings1[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings1\n"); | |
| exit(1); | |
| } | |
| k += 3; | |
| if (k >= 50) k = 0; | |
| } | |
| /* | |
| * Now do the same with the second pool of strings | |
| */ | |
| for (i = 0; seeds2[i] != NULL; i++) { | |
| strings2[i] = xmlStrdup((const xmlChar *) seeds2[i]); | |
| if (strings2[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings2\n"); | |
| exit(1); | |
| } | |
| } | |
| for (j = 0, k = 0;i < NB_STRINGS_MAX - NB_STRINGS_NS;i++,j++) { | |
| strings2[i] = xmlStrncatNew(strings2[j], strings2[k], -1); | |
| if (strings2[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings2\n"); | |
| exit(1); | |
| } | |
| if (j >= 50) { | |
| j = 0; | |
| k++; | |
| } | |
| } | |
| for (j = 0; (j < 50) && (i < NB_STRINGS_MAX); i++, j+=2) { | |
| strings2[i] = xmlStrncatNew(strings2[j], (const xmlChar *) ":", -1); | |
| if (strings2[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings2\n"); | |
| exit(1); | |
| } | |
| } | |
| for (j = NB_STRINGS_MAX - NB_STRINGS_NS, k = 0; | |
| i < NB_STRINGS_MAX;i++,j++) { | |
| strings2[i] = xmlStrncatNew(strings2[j], strings2[k], -1); | |
| if (strings2[i] == NULL) { | |
| fprintf(stderr, "Out of memory while generating strings2\n"); | |
| exit(1); | |
| } | |
| k += 3; | |
| if (k >= 50) k = 0; | |
| } | |
| } | |
| #ifdef WITH_PRINT | |
| static void print_strings(void) { | |
| int i; | |
| for (i = 0; i < NB_STRINGS_MAX;i++) { | |
| printf("%s\n", strings1[i]); | |
| } | |
| for (i = 0; i < NB_STRINGS_MAX;i++) { | |
| printf("%s\n", strings2[i]); | |
| } | |
| } | |
| #endif | |
| static void clean_strings(void) { | |
| int i; | |
| for (i = 0; i < NB_STRINGS_MAX; i++) { | |
| if (strings1[i] != NULL) /* really should not happen */ | |
| xmlFree(strings1[i]); | |
| } | |
| for (i = 0; i < NB_STRINGS_MAX; i++) { | |
| if (strings2[i] != NULL) /* really should not happen */ | |
| xmlFree(strings2[i]); | |
| } | |
| } | |
| /* | |
| * This tests the sub-dictionary support | |
| */ | |
| static int run_test2(xmlDictPtr parent) { | |
| int i, j; | |
| xmlDictPtr dict; | |
| int ret = 0; | |
| xmlChar prefix[40]; | |
| xmlChar *cur, *pref; | |
| const xmlChar *tmp; | |
| dict = xmlDictCreateSub(parent); | |
| if (dict == NULL) { | |
| fprintf(stderr, "Out of memory while creating sub-dictionary\n"); | |
| exit(1); | |
| } | |
| memset(test2, 0, sizeof(test2)); | |
| /* | |
| * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow | |
| * and we allocate all those doing the fast key computations | |
| * All the strings are based on a different seeds subset so we know | |
| * they are allocated in the main dictionary, not coming from the parent | |
| */ | |
| for (i = 0;i < NB_STRINGS_MIN;i++) { | |
| test2[i] = xmlDictLookup(dict, strings2[i], -1); | |
| if (test2[i] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| j = NB_STRINGS_MAX - NB_STRINGS_NS; | |
| /* ":foo" like strings2 */ | |
| for (i = 0;i < NB_STRINGS_MIN;i++, j++) { | |
| test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j])); | |
| if (test2[j] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* "a:foo" like strings2 */ | |
| j = NB_STRINGS_MAX - NB_STRINGS_MIN; | |
| for (i = 0;i < NB_STRINGS_MIN;i++, j++) { | |
| test2[j] = xmlDictLookup(dict, strings2[j], xmlStrlen(strings2[j])); | |
| if (test2[j] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings2[j]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * At this point allocate all the strings | |
| * the dictionary will grow in the process, reallocate more string tables | |
| * and switch to the better key generator | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (test2[i] != NULL) | |
| continue; | |
| test2[i] = xmlDictLookup(dict, strings2[i], -1); | |
| if (test2[i] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings2[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * Now we can start to test things, first that all strings2 belongs to | |
| * the dict, and that none of them was actually allocated in the parent | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (!xmlDictOwns(dict, test2[i])) { | |
| fprintf(stderr, "Failed ownership failure for '%s'\n", | |
| strings2[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| if (xmlDictOwns(parent, test2[i])) { | |
| fprintf(stderr, "Failed parent ownership failure for '%s'\n", | |
| strings2[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * Also verify that all strings from the parent are seen from the subdict | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (!xmlDictOwns(dict, test1[i])) { | |
| fprintf(stderr, "Failed sub-ownership failure for '%s'\n", | |
| strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * Then that another lookup to the string in sub will return the same | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (xmlDictLookup(dict, strings2[i], -1) != test2[i]) { | |
| fprintf(stderr, "Failed re-lookup check for %d, '%s'\n", | |
| i, strings2[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * But also that any lookup for a string in the parent will be provided | |
| * as in the parent | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) { | |
| fprintf(stderr, "Failed parent string lookup check for %d, '%s'\n", | |
| i, strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * check the QName lookups | |
| */ | |
| for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) { | |
| cur = strings2[i]; | |
| pref = &prefix[0]; | |
| while (*cur != ':') *pref++ = *cur++; | |
| cur++; | |
| *pref = 0; | |
| tmp = xmlDictQLookup(dict, &prefix[0], cur); | |
| if (tmp != test2[i]) { | |
| fprintf(stderr, "Failed lookup check for '%s':'%s'\n", | |
| &prefix[0], cur); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * check the QName lookups for strings from the parent | |
| */ | |
| for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) { | |
| cur = strings1[i]; | |
| pref = &prefix[0]; | |
| while (*cur != ':') *pref++ = *cur++; | |
| cur++; | |
| *pref = 0; | |
| tmp = xmlDictQLookup(dict, &prefix[0], cur); | |
| if (xmlDictQLookup(dict, &prefix[0], cur) != test1[i]) { | |
| fprintf(stderr, "Failed parent lookup check for '%s':'%s'\n", | |
| &prefix[0], cur); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| xmlDictFree(dict); | |
| return(ret); | |
| } | |
| /* | |
| * Test a single dictionary | |
| */ | |
| static int run_test1(void) { | |
| int i, j; | |
| xmlDictPtr dict; | |
| int ret = 0; | |
| xmlChar prefix[40]; | |
| xmlChar *cur, *pref; | |
| const xmlChar *tmp; | |
| dict = xmlDictCreate(); | |
| if (dict == NULL) { | |
| fprintf(stderr, "Out of memory while creating dictionary\n"); | |
| exit(1); | |
| } | |
| memset(test1, 0, sizeof(test1)); | |
| /* | |
| * Fill in NB_STRINGS_MIN, at this point the dictionary should not grow | |
| * and we allocate all those doing the fast key computations | |
| */ | |
| for (i = 0;i < NB_STRINGS_MIN;i++) { | |
| test1[i] = xmlDictLookup(dict, strings1[i], -1); | |
| if (test1[i] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| j = NB_STRINGS_MAX - NB_STRINGS_NS; | |
| /* ":foo" like strings1 */ | |
| for (i = 0;i < NB_STRINGS_MIN;i++, j++) { | |
| test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j])); | |
| if (test1[j] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* "a:foo" like strings1 */ | |
| j = NB_STRINGS_MAX - NB_STRINGS_MIN; | |
| for (i = 0;i < NB_STRINGS_MIN;i++, j++) { | |
| test1[j] = xmlDictLookup(dict, strings1[j], xmlStrlen(strings1[j])); | |
| if (test1[j] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings1[j]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * At this point allocate all the strings | |
| * the dictionary will grow in the process, reallocate more string tables | |
| * and switch to the better key generator | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (test1[i] != NULL) | |
| continue; | |
| test1[i] = xmlDictLookup(dict, strings1[i], -1); | |
| if (test1[i] == NULL) { | |
| fprintf(stderr, "Failed lookup for '%s'\n", strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * Now we can start to test things, first that all strings1 belongs to | |
| * the dict | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (!xmlDictOwns(dict, test1[i])) { | |
| fprintf(stderr, "Failed ownership failure for '%s'\n", | |
| strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * Then that another lookup to the string will return the same | |
| */ | |
| for (i = 0;i < NB_STRINGS_MAX;i++) { | |
| if (xmlDictLookup(dict, strings1[i], -1) != test1[i]) { | |
| fprintf(stderr, "Failed re-lookup check for %d, '%s'\n", | |
| i, strings1[i]); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| /* | |
| * More complex, check the QName lookups | |
| */ | |
| for (i = NB_STRINGS_MAX - NB_STRINGS_NS;i < NB_STRINGS_MAX;i++) { | |
| cur = strings1[i]; | |
| pref = &prefix[0]; | |
| while (*cur != ':') *pref++ = *cur++; | |
| cur++; | |
| *pref = 0; | |
| tmp = xmlDictQLookup(dict, &prefix[0], cur); | |
| if (tmp != test1[i]) { | |
| fprintf(stderr, "Failed lookup check for '%s':'%s'\n", | |
| &prefix[0], cur); | |
| ret = 1; | |
| nbErrors++; | |
| } | |
| } | |
| run_test2(dict); | |
| xmlDictFree(dict); | |
| return(ret); | |
| } | |
| int main(void) | |
| { | |
| int ret; | |
| LIBXML_TEST_VERSION | |
| fill_strings(); | |
| #ifdef WITH_PRINT | |
| print_strings(); | |
| #endif | |
| ret = run_test1(); | |
| if (ret == 0) { | |
| printf("dictionary tests succeeded %d strings\n", 2 * NB_STRINGS_MAX); | |
| } else { | |
| printf("dictionary tests failed with %d errors\n", nbErrors); | |
| } | |
| clean_strings(); | |
| xmlCleanupParser(); | |
| xmlMemoryDump(); | |
| return(ret); | |
| } |