| #include <stdlib.h> |
| #include <string.h> |
| #include <libxml/parser.h> |
| #include <libxml/dict.h> |
| |
| |
| /**** dictionary tests ****/ |
| |
| #ifdef __clang__ |
| #if __clang_major__ >= 12 |
| #define ATTRIBUTE_NO_SANITIZE_INTEGER \ |
| __attribute__ ((no_sanitize("unsigned-integer-overflow"))) \ |
| __attribute__ ((no_sanitize("unsigned-shift-base"))) |
| #else |
| #define ATTRIBUTE_NO_SANITIZE_INTEGER \ |
| __attribute__ ((no_sanitize("unsigned-integer-overflow"))) |
| #endif |
| #else |
| #define ATTRIBUTE_NO_SANITIZE_INTEGER |
| #endif |
| |
| /* #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_MAX 100000 |
| #define NB_STRINGS_NS 10000 |
| #define NB_STRINGS_PREFIX (NB_STRINGS_NS / 20) |
| #define NB_STRINGS_MIN 10 |
| |
| static xmlChar **strings1; |
| static xmlChar **strings2; |
| static const xmlChar **test1; |
| static const xmlChar **test2; |
| static int nbErrors = 0; |
| |
| static void |
| fill_string_pool(xmlChar **strings, const char **seeds) { |
| int i, j, k; |
| int start_ns = NB_STRINGS_MAX - NB_STRINGS_NS; |
| |
| /* |
| * 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; seeds[i] != NULL; i++) { |
| strings[i] = xmlStrdup((const xmlChar *) seeds[i]); |
| if (strings[i] == NULL) { |
| fprintf(stderr, "Out of memory while generating strings\n"); |
| exit(1); |
| } |
| } |
| for (j = 0, k = 0; i < start_ns; i++) { |
| strings[i] = xmlStrncatNew(strings[j], strings[k], -1); |
| if (strings[i] == NULL) { |
| fprintf(stderr, "Out of memory while generating strings\n"); |
| exit(1); |
| } |
| if (xmlStrlen(strings[i]) > 30) { |
| fprintf(stderr, "### %s %s\n", strings[start_ns+j], strings[k]); |
| abort(); |
| } |
| j++; |
| if (j >= 50) { |
| j = 0; |
| k++; |
| } |
| } |
| for (j = 0, k = 0; (j < NB_STRINGS_PREFIX) && (i < NB_STRINGS_MAX); |
| i++, j++) { |
| strings[i] = xmlStrncatNew(strings[k], (const xmlChar *) ":", -1); |
| if (strings[i] == NULL) { |
| fprintf(stderr, "Out of memory while generating strings\n"); |
| exit(1); |
| } |
| k += 1; |
| if (k >= start_ns) k = 0; |
| } |
| for (j = 0, k = 0; i < NB_STRINGS_MAX; i++) { |
| strings[i] = xmlStrncatNew(strings[start_ns+j], strings[k], -1); |
| if (strings[i] == NULL) { |
| fprintf(stderr, "Out of memory while generating strings\n"); |
| exit(1); |
| } |
| j++; |
| if (j >= NB_STRINGS_PREFIX) j = 0; |
| k += 5; |
| if (k >= start_ns) 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 |
| test_subdict(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); |
| } |
| /* Cast to avoid buggy warning on MSVC. */ |
| memset((void *) 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 |
| test_dict(xmlDict *dict) { |
| int i, j; |
| int ret = 0; |
| xmlChar prefix[40]; |
| xmlChar *cur, *pref; |
| const xmlChar *tmp; |
| |
| /* Cast to avoid buggy warning on MSVC. */ |
| memset((void *) 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++; |
| } |
| } |
| |
| return(ret); |
| } |
| |
| static int |
| testall_dict(void) { |
| xmlDictPtr dict; |
| int ret = 0; |
| |
| strings1 = xmlMalloc(NB_STRINGS_MAX * sizeof(strings1[0])); |
| memset(strings1, 0, NB_STRINGS_MAX * sizeof(strings1[0])); |
| strings2 = xmlMalloc(NB_STRINGS_MAX * sizeof(strings2[0])); |
| memset(strings2, 0, NB_STRINGS_MAX * sizeof(strings2[0])); |
| test1 = xmlMalloc(NB_STRINGS_MAX * sizeof(test1[0])); |
| memset(test1, 0, NB_STRINGS_MAX * sizeof(test1[0])); |
| test2 = xmlMalloc(NB_STRINGS_MAX * sizeof(test2[0])); |
| memset(test2, 0, NB_STRINGS_MAX * sizeof(test2[0])); |
| |
| fill_string_pool(strings1, seeds1); |
| fill_string_pool(strings2, seeds2); |
| #ifdef WITH_PRINT |
| print_strings(); |
| #endif |
| |
| dict = xmlDictCreate(); |
| if (dict == NULL) { |
| fprintf(stderr, "Out of memory while creating dictionary\n"); |
| exit(1); |
| } |
| if (test_dict(dict) != 0) { |
| ret = 1; |
| } |
| if (test_subdict(dict) != 0) { |
| ret = 1; |
| } |
| xmlDictFree(dict); |
| |
| clean_strings(); |
| xmlFree(strings1); |
| xmlFree(strings2); |
| xmlFree(test1); |
| xmlFree(test2); |
| |
| return ret; |
| } |
| |
| |
| /**** Hash table tests ****/ |
| |
| static unsigned |
| rng_state[2] = { 123, 456 }; |
| |
| #define HASH_ROL(x,n) ((x) << (n) | ((x) & 0xFFFFFFFF) >> (32 - (n))) |
| |
| ATTRIBUTE_NO_SANITIZE_INTEGER |
| static unsigned |
| my_rand(unsigned max) { |
| unsigned s0 = rng_state[0]; |
| unsigned s1 = rng_state[1]; |
| unsigned result = HASH_ROL(s0 * 0x9E3779BB, 5) * 5; |
| |
| s1 ^= s0; |
| rng_state[0] = HASH_ROL(s0, 26) ^ s1 ^ (s1 << 9); |
| rng_state[1] = HASH_ROL(s1, 13); |
| |
| return((result & 0xFFFFFFFF) % max); |
| } |
| |
| static xmlChar * |
| gen_random_string(xmlChar id) { |
| unsigned size = my_rand(64) + 1; |
| unsigned id_pos = my_rand(size); |
| size_t j; |
| |
| xmlChar *str = xmlMalloc(size + 1); |
| for (j = 0; j < size; j++) { |
| str[j] = 'a' + my_rand(26); |
| } |
| str[id_pos] = id; |
| str[size] = 0; |
| |
| /* Generate QName in 75% of cases */ |
| if (size > 3 && my_rand(4) > 0) { |
| unsigned colon_pos = my_rand(size - 3) + 1; |
| |
| if (colon_pos >= id_pos) |
| colon_pos++; |
| str[colon_pos] = ':'; |
| } |
| |
| return str; |
| } |
| |
| typedef struct { |
| xmlChar **strings; |
| size_t num_entries; |
| size_t num_keys; |
| size_t num_strings; |
| size_t index; |
| xmlChar id; |
| } StringPool; |
| |
| static StringPool * |
| pool_new(size_t num_entries, size_t num_keys, xmlChar id) { |
| StringPool *ret; |
| size_t num_strings; |
| |
| ret = xmlMalloc(sizeof(*ret)); |
| ret->num_entries = num_entries; |
| ret->num_keys = num_keys; |
| num_strings = num_entries * num_keys; |
| ret->strings = xmlMalloc(num_strings * sizeof(ret->strings[0])); |
| memset(ret->strings, 0, num_strings * sizeof(ret->strings[0])); |
| ret->num_strings = num_strings; |
| ret->index = 0; |
| ret->id = id; |
| |
| return ret; |
| } |
| |
| static void |
| pool_free(StringPool *pool) { |
| size_t i; |
| |
| for (i = 0; i < pool->num_strings; i++) { |
| xmlFree(pool->strings[i]); |
| } |
| xmlFree(pool->strings); |
| xmlFree(pool); |
| } |
| |
| static int |
| pool_done(StringPool *pool) { |
| return pool->index >= pool->num_strings; |
| } |
| |
| static void |
| pool_reset(StringPool *pool) { |
| pool->index = 0; |
| } |
| |
| static int |
| pool_bulk_insert(StringPool *pool, xmlHashTablePtr hash, size_t num) { |
| size_t i, j; |
| int ret = 0; |
| |
| for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) { |
| xmlChar *str[3]; |
| size_t k; |
| |
| while (1) { |
| xmlChar tmp_key[1]; |
| int res; |
| |
| for (k = 0; k < pool->num_keys; k++) |
| str[k] = gen_random_string(pool->id); |
| |
| switch (pool->num_keys) { |
| case 1: |
| res = xmlHashAddEntry(hash, str[0], tmp_key); |
| if (res == 0 && |
| xmlHashUpdateEntry(hash, str[0], str[0], NULL) != 0) |
| ret = -1; |
| break; |
| case 2: |
| res = xmlHashAddEntry2(hash, str[0], str[1], tmp_key); |
| if (res == 0 && |
| xmlHashUpdateEntry2(hash, str[0], str[1], str[0], |
| NULL) != 0) |
| ret = -1; |
| break; |
| case 3: |
| res = xmlHashAddEntry3(hash, str[0], str[1], str[2], |
| tmp_key); |
| if (res == 0 && |
| xmlHashUpdateEntry3(hash, str[0], str[1], str[2], |
| str[0], NULL) != 0) |
| ret = -1; |
| break; |
| } |
| |
| if (res == 0) |
| break; |
| for (k = 0; k < pool->num_keys; k++) |
| xmlFree(str[k]); |
| } |
| |
| for (k = 0; k < pool->num_keys; k++) |
| pool->strings[i++] = str[k]; |
| } |
| |
| pool->index = i; |
| return ret; |
| } |
| |
| static xmlChar * |
| hash_qlookup(xmlHashTable *hash, xmlChar **names, size_t num_keys) { |
| xmlChar *prefix[3]; |
| const xmlChar *local[3]; |
| xmlChar *res; |
| size_t i; |
| |
| for (i = 0; i < 3; ++i) { |
| if (i >= num_keys) { |
| prefix[i] = NULL; |
| local[i] = NULL; |
| } else { |
| const xmlChar *name = names[i]; |
| const xmlChar *colon = BAD_CAST strchr((const char *) name, ':'); |
| |
| if (colon == NULL) { |
| prefix[i] = NULL; |
| local[i] = name; |
| } else { |
| prefix[i] = xmlStrndup(name, colon - name); |
| local[i] = &colon[1]; |
| } |
| } |
| } |
| |
| res = xmlHashQLookup3(hash, prefix[0], local[0], prefix[1], local[1], |
| prefix[2], local[2]); |
| |
| for (i = 0; i < 3; ++i) |
| xmlFree(prefix[i]); |
| |
| return res; |
| } |
| |
| static int |
| pool_bulk_lookup(StringPool *pool, xmlHashTablePtr hash, size_t num, |
| int existing) { |
| size_t i, j; |
| int ret = 0; |
| |
| for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) { |
| xmlChar **str = &pool->strings[i]; |
| int q; |
| |
| for (q = 0; q < 2; q++) { |
| xmlChar *res = NULL; |
| |
| if (q) { |
| res = hash_qlookup(hash, str, pool->num_keys); |
| } else { |
| switch (pool->num_keys) { |
| case 1: |
| res = xmlHashLookup(hash, str[0]); |
| break; |
| case 2: |
| res = xmlHashLookup2(hash, str[0], str[1]); |
| break; |
| case 3: |
| res = xmlHashLookup3(hash, str[0], str[1], str[2]); |
| break; |
| } |
| } |
| |
| if (existing) { |
| if (res != str[0]) |
| ret = -1; |
| } else { |
| if (res != NULL) |
| ret = -1; |
| } |
| } |
| |
| i += pool->num_keys; |
| } |
| |
| pool->index = i; |
| return ret; |
| } |
| |
| static int |
| pool_bulk_remove(StringPool *pool, xmlHashTablePtr hash, size_t num) { |
| size_t i, j; |
| int ret = 0; |
| |
| for (i = pool->index, j = 0; i < pool->num_strings && j < num; j++) { |
| xmlChar **str = &pool->strings[i]; |
| int res = -1; |
| |
| switch (pool->num_keys) { |
| case 1: |
| res = xmlHashRemoveEntry(hash, str[0], NULL); |
| break; |
| case 2: |
| res = xmlHashRemoveEntry2(hash, str[0], str[1], NULL); |
| break; |
| case 3: |
| res = xmlHashRemoveEntry3(hash, str[0], str[1], str[2], NULL); |
| break; |
| } |
| |
| if (res != 0) |
| ret = -1; |
| |
| i += pool->num_keys; |
| } |
| |
| pool->index = i; |
| return ret; |
| } |
| |
| static int |
| test_hash(size_t num_entries, size_t num_keys, int use_dict) { |
| xmlDict *dict = NULL; |
| xmlHashTable *hash; |
| StringPool *pool1, *pool2; |
| int ret = 0; |
| |
| if (use_dict) { |
| dict = xmlDictCreate(); |
| hash = xmlHashCreateDict(0, dict); |
| } else { |
| hash = xmlHashCreate(0); |
| } |
| pool1 = pool_new(num_entries, num_keys, '1'); |
| pool2 = pool_new(num_entries, num_keys, '2'); |
| |
| /* Insert all strings from pool2 and about half of pool1. */ |
| while (!pool_done(pool2)) { |
| if (pool_bulk_insert(pool1, hash, my_rand(50)) != 0) { |
| fprintf(stderr, "pool1: hash insert failed\n"); |
| ret = 1; |
| } |
| if (pool_bulk_insert(pool2, hash, my_rand(100)) != 0) { |
| fprintf(stderr, "pool1: hash insert failed\n"); |
| ret = 1; |
| } |
| } |
| |
| /* Check existing entries */ |
| pool_reset(pool2); |
| if (pool_bulk_lookup(pool2, hash, pool2->num_entries, 1) != 0) { |
| fprintf(stderr, "pool2: hash lookup failed\n"); |
| ret = 1; |
| } |
| |
| /* Remove all strings from pool2 and insert the rest of pool1. */ |
| pool_reset(pool2); |
| while (!pool_done(pool1) || !pool_done(pool2)) { |
| if (pool_bulk_insert(pool1, hash, my_rand(50)) != 0) { |
| fprintf(stderr, "pool1: hash insert failed\n"); |
| ret = 1; |
| } |
| if (pool_bulk_remove(pool2, hash, my_rand(100)) != 0) { |
| fprintf(stderr, "pool2: hash remove failed\n"); |
| ret = 1; |
| } |
| } |
| |
| /* Check existing entries */ |
| pool_reset(pool1); |
| if (pool_bulk_lookup(pool1, hash, pool1->num_entries, 1) != 0) { |
| fprintf(stderr, "pool1: hash lookup failed\n"); |
| ret = 1; |
| } |
| |
| /* Check removed entries */ |
| pool_reset(pool2); |
| if (pool_bulk_lookup(pool2, hash, pool2->num_entries, 0) != 0) { |
| fprintf(stderr, "pool2: hash lookup succeeded unexpectedly\n"); |
| ret = 1; |
| } |
| |
| pool_free(pool1); |
| pool_free(pool2); |
| xmlHashFree(hash, NULL); |
| xmlDictFree(dict); |
| |
| return ret; |
| } |
| |
| static int |
| testall_hash(void) { |
| size_t num_keys; |
| |
| for (num_keys = 1; num_keys <= 3; num_keys++) { |
| size_t num_strings; |
| size_t max_strings = num_keys == 1 ? 100000 : 1000; |
| |
| for (num_strings = 10; num_strings <= max_strings; num_strings *= 10) { |
| size_t reps, i; |
| |
| reps = 1000 / num_strings; |
| if (reps == 0) |
| reps = 1; |
| |
| for (i = 0; i < reps; i++) { |
| if (test_hash(num_strings, num_keys, /* use_dict */ 0) != 0) |
| return(1); |
| } |
| |
| if (test_hash(num_strings, num_keys, /* use_dict */ 1) != 0) |
| return(1); |
| } |
| } |
| |
| return(0); |
| } |
| |
| |
| /**** main ****/ |
| |
| int |
| main(void) { |
| int ret = 0; |
| |
| LIBXML_TEST_VERSION |
| |
| if (testall_dict() != 0) { |
| fprintf(stderr, "dictionary tests failed\n"); |
| ret = 1; |
| } |
| if (testall_hash() != 0) { |
| fprintf(stderr, "hash tests failed\n"); |
| ret = 1; |
| } |
| |
| xmlCleanupParser(); |
| return(ret); |
| } |