testdict.c 21 KB

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