hash.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. /*
  2. * hash.c: hash tables
  3. *
  4. * Hash table with open addressing, linear probing and
  5. * Robin Hood reordering.
  6. *
  7. * See Copyright for the status of this software.
  8. */
  9. #define IN_LIBXML
  10. #include "libxml.h"
  11. #include <string.h>
  12. #include <limits.h>
  13. #include <libxml/parser.h>
  14. #include <libxml/hash.h>
  15. #include <libxml/dict.h>
  16. #include <libxml/xmlmemory.h>
  17. #include <libxml/xmlstring.h>
  18. #include "private/dict.h"
  19. #ifndef SIZE_MAX
  20. #define SIZE_MAX ((size_t) -1)
  21. #endif
  22. #define MAX_FILL_NUM 7
  23. #define MAX_FILL_DENOM 8
  24. #define MIN_HASH_SIZE 8
  25. #define MAX_HASH_SIZE (1u << 31)
  26. /*
  27. * A single entry in the hash table
  28. */
  29. typedef struct {
  30. unsigned hashValue; /* 0 means unoccupied, occupied entries have the
  31. * MAX_HASH_SIZE bit set to 1 */
  32. xmlChar *key;
  33. xmlChar *key2; /* TODO: Don't allocate possibly empty keys */
  34. xmlChar *key3;
  35. void *payload;
  36. } xmlHashEntry;
  37. /*
  38. * The entire hash table
  39. */
  40. struct _xmlHashTable {
  41. xmlHashEntry *table;
  42. unsigned size; /* power of two */
  43. unsigned nbElems;
  44. xmlDictPtr dict;
  45. unsigned randomSeed;
  46. };
  47. static int
  48. xmlHashGrow(xmlHashTablePtr hash, unsigned size);
  49. ATTRIBUTE_NO_SANITIZE_INTEGER
  50. static unsigned
  51. xmlHashValue(unsigned seed, const xmlChar *key, const xmlChar *key2,
  52. const xmlChar *key3, size_t *lengths) {
  53. unsigned h1, h2;
  54. size_t i;
  55. HASH_INIT(h1, h2, seed);
  56. for (i = 0; key[i] != 0; i++) {
  57. HASH_UPDATE(h1, h2, key[i]);
  58. }
  59. if (lengths)
  60. lengths[0] = i;
  61. HASH_UPDATE(h1, h2, 0);
  62. if (key2 != NULL) {
  63. for (i = 0; key2[i] != 0; i++) {
  64. HASH_UPDATE(h1, h2, key2[i]);
  65. }
  66. if (lengths)
  67. lengths[1] = i;
  68. }
  69. HASH_UPDATE(h1, h2, 0);
  70. if (key3 != NULL) {
  71. for (i = 0; key3[i] != 0; i++) {
  72. HASH_UPDATE(h1, h2, key3[i]);
  73. }
  74. if (lengths)
  75. lengths[2] = i;
  76. }
  77. HASH_FINISH(h1, h2);
  78. return(h2);
  79. }
  80. ATTRIBUTE_NO_SANITIZE_INTEGER
  81. static unsigned
  82. xmlHashQNameValue(unsigned seed,
  83. const xmlChar *prefix, const xmlChar *name,
  84. const xmlChar *prefix2, const xmlChar *name2,
  85. const xmlChar *prefix3, const xmlChar *name3) {
  86. unsigned h1, h2, ch;
  87. HASH_INIT(h1, h2, seed);
  88. if (prefix != NULL) {
  89. while ((ch = *prefix++) != 0) {
  90. HASH_UPDATE(h1, h2, ch);
  91. }
  92. HASH_UPDATE(h1, h2, ':');
  93. }
  94. if (name != NULL) {
  95. while ((ch = *name++) != 0) {
  96. HASH_UPDATE(h1, h2, ch);
  97. }
  98. }
  99. HASH_UPDATE(h1, h2, 0);
  100. if (prefix2 != NULL) {
  101. while ((ch = *prefix2++) != 0) {
  102. HASH_UPDATE(h1, h2, ch);
  103. }
  104. HASH_UPDATE(h1, h2, ':');
  105. }
  106. if (name2 != NULL) {
  107. while ((ch = *name2++) != 0) {
  108. HASH_UPDATE(h1, h2, ch);
  109. }
  110. }
  111. HASH_UPDATE(h1, h2, 0);
  112. if (prefix3 != NULL) {
  113. while ((ch = *prefix3++) != 0) {
  114. HASH_UPDATE(h1, h2, ch);
  115. }
  116. HASH_UPDATE(h1, h2, ':');
  117. }
  118. if (name3 != NULL) {
  119. while ((ch = *name3++) != 0) {
  120. HASH_UPDATE(h1, h2, ch);
  121. }
  122. }
  123. HASH_FINISH(h1, h2);
  124. return(h2);
  125. }
  126. /**
  127. * xmlHashCreate:
  128. * @size: initial size of the hash table
  129. *
  130. * Create a new hash table. Set size to zero if the number of entries
  131. * can't be estimated.
  132. *
  133. * Returns the newly created object, or NULL if a memory allocation failed.
  134. */
  135. xmlHashTablePtr
  136. xmlHashCreate(int size) {
  137. xmlHashTablePtr hash;
  138. xmlInitParser();
  139. hash = xmlMalloc(sizeof(*hash));
  140. if (hash == NULL)
  141. return(NULL);
  142. hash->dict = NULL;
  143. hash->size = 0;
  144. hash->table = NULL;
  145. hash->nbElems = 0;
  146. hash->randomSeed = xmlRandom();
  147. #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
  148. hash->randomSeed = 0;
  149. #endif
  150. /*
  151. * Unless a larger size is passed, the backing table is created
  152. * lazily with MIN_HASH_SIZE capacity. In practice, there are many
  153. * hash tables which are never filled.
  154. */
  155. if (size > MIN_HASH_SIZE) {
  156. unsigned newSize = MIN_HASH_SIZE * 2;
  157. while ((newSize < (unsigned) size) && (newSize < MAX_HASH_SIZE))
  158. newSize *= 2;
  159. if (xmlHashGrow(hash, newSize) != 0) {
  160. xmlFree(hash);
  161. return(NULL);
  162. }
  163. }
  164. return(hash);
  165. }
  166. /**
  167. * xmlHashCreateDict:
  168. * @size: the size of the hash table
  169. * @dict: a dictionary to use for the hash
  170. *
  171. * Create a new hash table backed by a dictionary. This can reduce
  172. * resource usage considerably if most keys passed to API functions
  173. * originate from this dictionary.
  174. *
  175. * Returns the newly created object, or NULL if a memory allocation failed.
  176. */
  177. xmlHashTablePtr
  178. xmlHashCreateDict(int size, xmlDictPtr dict) {
  179. xmlHashTablePtr hash;
  180. hash = xmlHashCreate(size);
  181. if (hash != NULL) {
  182. hash->dict = dict;
  183. xmlDictReference(dict);
  184. }
  185. return(hash);
  186. }
  187. /**
  188. * xmlHashFree:
  189. * @hash: hash table
  190. * @dealloc: deallocator function or NULL
  191. *
  192. * Free the hash and its contents. The payload is deallocated with
  193. * @dealloc if provided.
  194. */
  195. void
  196. xmlHashFree(xmlHashTablePtr hash, xmlHashDeallocator dealloc) {
  197. if (hash == NULL)
  198. return;
  199. if (hash->table) {
  200. const xmlHashEntry *end = &hash->table[hash->size];
  201. const xmlHashEntry *entry;
  202. for (entry = hash->table; entry < end; entry++) {
  203. if (entry->hashValue == 0)
  204. continue;
  205. if ((dealloc != NULL) && (entry->payload != NULL))
  206. dealloc(entry->payload, entry->key);
  207. if (hash->dict == NULL) {
  208. if (entry->key)
  209. xmlFree(entry->key);
  210. if (entry->key2)
  211. xmlFree(entry->key2);
  212. if (entry->key3)
  213. xmlFree(entry->key3);
  214. }
  215. }
  216. xmlFree(hash->table);
  217. }
  218. if (hash->dict)
  219. xmlDictFree(hash->dict);
  220. xmlFree(hash);
  221. }
  222. /**
  223. * xmlFastStrEqual:
  224. * @s1: string
  225. * @s2: string
  226. *
  227. * Compare two strings for equality, allowing NULL values.
  228. */
  229. static int
  230. xmlFastStrEqual(const xmlChar *s1, const xmlChar *s2) {
  231. if (s1 == NULL)
  232. return(s2 == NULL);
  233. else
  234. return((s2 != NULL) &&
  235. (strcmp((const char *) s1, (const char *) s2) == 0));
  236. }
  237. /**
  238. * xmlHashFindEntry:
  239. * @hash: hash table, non-NULL, size > 0
  240. * @key: first string key, non-NULL
  241. * @key2: second string key
  242. * @key3: third string key
  243. * @hashValue: valid hash value of keys
  244. * @pfound: result of search
  245. *
  246. * Try to find a matching hash table entry. If an entry was found, set
  247. * @found to 1 and return the entry. Otherwise, set @found to 0 and return
  248. * the location where a new entry should be inserted.
  249. */
  250. ATTRIBUTE_NO_SANITIZE_INTEGER
  251. static xmlHashEntry *
  252. xmlHashFindEntry(const xmlHashTable *hash, const xmlChar *key,
  253. const xmlChar *key2, const xmlChar *key3,
  254. unsigned hashValue, int *pfound) {
  255. xmlHashEntry *entry;
  256. unsigned mask, pos, displ;
  257. int found = 0;
  258. mask = hash->size - 1;
  259. pos = hashValue & mask;
  260. entry = &hash->table[pos];
  261. if (entry->hashValue != 0) {
  262. /*
  263. * Robin hood hashing: abort if the displacement of the entry
  264. * is smaller than the displacement of the key we look for.
  265. * This also stops at the correct position when inserting.
  266. */
  267. displ = 0;
  268. hashValue |= MAX_HASH_SIZE;
  269. do {
  270. if (entry->hashValue == hashValue) {
  271. if (hash->dict) {
  272. if ((entry->key == key) &&
  273. (entry->key2 == key2) &&
  274. (entry->key3 == key3)) {
  275. found = 1;
  276. break;
  277. }
  278. }
  279. if ((strcmp((const char *) entry->key,
  280. (const char *) key) == 0) &&
  281. (xmlFastStrEqual(entry->key2, key2)) &&
  282. (xmlFastStrEqual(entry->key3, key3))) {
  283. found = 1;
  284. break;
  285. }
  286. }
  287. displ++;
  288. pos++;
  289. entry++;
  290. if ((pos & mask) == 0)
  291. entry = hash->table;
  292. } while ((entry->hashValue != 0) &&
  293. (((pos - entry->hashValue) & mask) >= displ));
  294. }
  295. *pfound = found;
  296. return(entry);
  297. }
  298. /**
  299. * xmlHashGrow:
  300. * @hash: hash table
  301. * @size: new size of the hash table
  302. *
  303. * Resize the hash table.
  304. *
  305. * Returns 0 in case of success, -1 if a memory allocation failed.
  306. */
  307. static int
  308. xmlHashGrow(xmlHashTablePtr hash, unsigned size) {
  309. const xmlHashEntry *oldentry, *oldend, *end;
  310. xmlHashEntry *table;
  311. unsigned oldsize, i;
  312. /* Add 0 to avoid spurious -Wtype-limits warning on 64-bit GCC */
  313. if ((size_t) size + 0 > SIZE_MAX / sizeof(table[0]))
  314. return(-1);
  315. table = xmlMalloc(size * sizeof(table[0]));
  316. if (table == NULL)
  317. return(-1);
  318. memset(table, 0, size * sizeof(table[0]));
  319. oldsize = hash->size;
  320. if (oldsize == 0)
  321. goto done;
  322. oldend = &hash->table[oldsize];
  323. end = &table[size];
  324. /*
  325. * Robin Hood sorting order is maintained if we
  326. *
  327. * - compute hash indices with modulo
  328. * - resize by an integer factor
  329. * - start to copy from the beginning of a probe sequence
  330. */
  331. oldentry = hash->table;
  332. while (oldentry->hashValue != 0) {
  333. if (++oldentry >= oldend)
  334. oldentry = hash->table;
  335. }
  336. for (i = 0; i < oldsize; i++) {
  337. if (oldentry->hashValue != 0) {
  338. xmlHashEntry *entry = &table[oldentry->hashValue & (size - 1)];
  339. while (entry->hashValue != 0) {
  340. if (++entry >= end)
  341. entry = table;
  342. }
  343. *entry = *oldentry;
  344. }
  345. if (++oldentry >= oldend)
  346. oldentry = hash->table;
  347. }
  348. xmlFree(hash->table);
  349. done:
  350. hash->table = table;
  351. hash->size = size;
  352. return(0);
  353. }
  354. /**
  355. * xmlHashUpdateInternal:
  356. * @hash: hash table
  357. * @key: first string key
  358. * @key2: second string key
  359. * @key3: third string key
  360. * @payload: pointer to the payload
  361. * @dealloc: deallocator function for replaced item or NULL
  362. * @update: whether existing entries should be updated
  363. *
  364. * Internal function to add or update hash entries.
  365. */
  366. ATTRIBUTE_NO_SANITIZE_INTEGER
  367. static int
  368. xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key,
  369. const xmlChar *key2, const xmlChar *key3,
  370. void *payload, xmlHashDeallocator dealloc, int update) {
  371. xmlChar *copy, *copy2, *copy3;
  372. xmlHashEntry *entry = NULL;
  373. size_t lengths[3];
  374. unsigned hashValue;
  375. int found = 0;
  376. if ((hash == NULL) || (key == NULL))
  377. return(-1);
  378. /*
  379. * Check for an existing entry
  380. */
  381. hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, lengths);
  382. if (hash->size > 0)
  383. entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
  384. if (found) {
  385. if (update) {
  386. if (dealloc)
  387. dealloc(entry->payload, entry->key);
  388. entry->payload = payload;
  389. return(0);
  390. } else {
  391. /*
  392. * xmlHashAddEntry found an existing entry.
  393. *
  394. * TODO: We should return a different error code here to
  395. * distinguish from malloc failures.
  396. */
  397. return(-1);
  398. }
  399. }
  400. /*
  401. * Grow the hash table if needed
  402. */
  403. if (hash->nbElems + 1 > hash->size / MAX_FILL_DENOM * MAX_FILL_NUM) {
  404. unsigned newSize, mask, displ, pos;
  405. if (hash->size == 0) {
  406. newSize = MIN_HASH_SIZE;
  407. } else {
  408. /* This guarantees that nbElems < INT_MAX */
  409. if (hash->size >= MAX_HASH_SIZE)
  410. return(-1);
  411. newSize = hash->size * 2;
  412. }
  413. if (xmlHashGrow(hash, newSize) != 0)
  414. return(-1);
  415. /*
  416. * Find new entry
  417. */
  418. mask = hash->size - 1;
  419. displ = 0;
  420. pos = hashValue & mask;
  421. entry = &hash->table[pos];
  422. if (entry->hashValue != 0) {
  423. do {
  424. displ++;
  425. pos++;
  426. entry++;
  427. if ((pos & mask) == 0)
  428. entry = hash->table;
  429. } while ((entry->hashValue != 0) &&
  430. ((pos - entry->hashValue) & mask) >= displ);
  431. }
  432. }
  433. /*
  434. * Copy keys
  435. */
  436. if (hash->dict != NULL) {
  437. if (xmlDictOwns(hash->dict, key)) {
  438. copy = (xmlChar *) key;
  439. } else {
  440. copy = (xmlChar *) xmlDictLookup(hash->dict, key, -1);
  441. if (copy == NULL)
  442. return(-1);
  443. }
  444. if ((key2 == NULL) || (xmlDictOwns(hash->dict, key2))) {
  445. copy2 = (xmlChar *) key2;
  446. } else {
  447. copy2 = (xmlChar *) xmlDictLookup(hash->dict, key2, -1);
  448. if (copy2 == NULL)
  449. return(-1);
  450. }
  451. if ((key3 == NULL) || (xmlDictOwns(hash->dict, key3))) {
  452. copy3 = (xmlChar *) key3;
  453. } else {
  454. copy3 = (xmlChar *) xmlDictLookup(hash->dict, key3, -1);
  455. if (copy3 == NULL)
  456. return(-1);
  457. }
  458. } else {
  459. copy = xmlMalloc(lengths[0] + 1);
  460. if (copy == NULL)
  461. return(-1);
  462. memcpy(copy, key, lengths[0] + 1);
  463. if (key2 != NULL) {
  464. copy2 = xmlMalloc(lengths[1] + 1);
  465. if (copy2 == NULL) {
  466. xmlFree(copy);
  467. return(-1);
  468. }
  469. memcpy(copy2, key2, lengths[1] + 1);
  470. } else {
  471. copy2 = NULL;
  472. }
  473. if (key3 != NULL) {
  474. copy3 = xmlMalloc(lengths[2] + 1);
  475. if (copy3 == NULL) {
  476. xmlFree(copy);
  477. xmlFree(copy2);
  478. return(-1);
  479. }
  480. memcpy(copy3, key3, lengths[2] + 1);
  481. } else {
  482. copy3 = NULL;
  483. }
  484. }
  485. /*
  486. * Shift the remainder of the probe sequence to the right
  487. */
  488. if (entry->hashValue != 0) {
  489. const xmlHashEntry *end = &hash->table[hash->size];
  490. const xmlHashEntry *cur = entry;
  491. do {
  492. cur++;
  493. if (cur >= end)
  494. cur = hash->table;
  495. } while (cur->hashValue != 0);
  496. if (cur < entry) {
  497. /*
  498. * If we traversed the end of the buffer, handle the part
  499. * at the start of the buffer.
  500. */
  501. memmove(&hash->table[1], hash->table,
  502. (char *) cur - (char *) hash->table);
  503. cur = end - 1;
  504. hash->table[0] = *cur;
  505. }
  506. memmove(&entry[1], entry, (char *) cur - (char *) entry);
  507. }
  508. /*
  509. * Populate entry
  510. */
  511. entry->key = copy;
  512. entry->key2 = copy2;
  513. entry->key3 = copy3;
  514. entry->payload = payload;
  515. /* OR with MAX_HASH_SIZE to make sure that the value is non-zero */
  516. entry->hashValue = hashValue | MAX_HASH_SIZE;
  517. hash->nbElems++;
  518. return(0);
  519. }
  520. /**
  521. * xmlHashDefaultDeallocator:
  522. * @entry: hash table entry
  523. * @key: the entry's string key
  524. *
  525. * Free a hash table entry with xmlFree.
  526. */
  527. void
  528. xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) {
  529. xmlFree(entry);
  530. }
  531. /**
  532. * xmlHashAddEntry:
  533. * @hash: hash table
  534. * @key: string key
  535. * @payload: pointer to the payload
  536. *
  537. * Add a hash table entry. If an entry with this key already exists,
  538. * payload will not be updated and -1 is returned. This return value
  539. * can't be distinguished from out-of-memory errors, so this function
  540. * should be used with care.
  541. *
  542. * Returns 0 on success and -1 in case of error.
  543. */
  544. int
  545. xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) {
  546. return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0));
  547. }
  548. /**
  549. * xmlHashAddEntry2:
  550. * @hash: hash table
  551. * @key: first string key
  552. * @key2: second string key
  553. * @payload: pointer to the payload
  554. *
  555. * Add a hash table entry with two strings as key.
  556. *
  557. * See xmlHashAddEntry.
  558. *
  559. * Returns 0 on success and -1 in case of error.
  560. */
  561. int
  562. xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key,
  563. const xmlChar *key2, void *payload) {
  564. return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0));
  565. }
  566. /**
  567. * xmlHashAddEntry3:
  568. * @hash: hash table
  569. * @key: first string key
  570. * @key2: second string key
  571. * @key3: third string key
  572. * @payload: pointer to the payload
  573. *
  574. * Add a hash table entry with three strings as key.
  575. *
  576. * See xmlHashAddEntry.
  577. *
  578. * Returns 0 on success and -1 in case of error.
  579. */
  580. int
  581. xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key,
  582. const xmlChar *key2, const xmlChar *key3,
  583. void *payload) {
  584. return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0));
  585. }
  586. /**
  587. * xmlHashUpdateEntry:
  588. * @hash: hash table
  589. * @key: string key
  590. * @payload: pointer to the payload
  591. * @dealloc: deallocator function for replaced item or NULL
  592. *
  593. * Add a hash table entry. If an entry with this key already exists,
  594. * the old payload will be freed and updated with the new value.
  595. *
  596. * Returns 0 in case of success, -1 if a memory allocation failed.
  597. */
  598. int
  599. xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key,
  600. void *payload, xmlHashDeallocator dealloc) {
  601. return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload,
  602. dealloc, 1));
  603. }
  604. /**
  605. * xmlHashUpdateEntry2:
  606. * @hash: hash table
  607. * @key: first string key
  608. * @key2: second string key
  609. * @payload: pointer to the payload
  610. * @dealloc: deallocator function for replaced item or NULL
  611. *
  612. * Add a hash table entry with two strings as key.
  613. *
  614. * See xmlHashUpdateEntry.
  615. *
  616. * Returns 0 on success and -1 in case of error.
  617. */
  618. int
  619. xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key,
  620. const xmlChar *key2, void *payload,
  621. xmlHashDeallocator dealloc) {
  622. return(xmlHashUpdateInternal(hash, key, key2, NULL, payload,
  623. dealloc, 1));
  624. }
  625. /**
  626. * xmlHashUpdateEntry3:
  627. * @hash: hash table
  628. * @key: first string key
  629. * @key2: second string key
  630. * @key3: third string key
  631. * @payload: pointer to the payload
  632. * @dealloc: deallocator function for replaced item or NULL
  633. *
  634. * Add a hash table entry with three strings as key.
  635. *
  636. * See xmlHashUpdateEntry.
  637. *
  638. * Returns 0 on success and -1 in case of error.
  639. */
  640. int
  641. xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key,
  642. const xmlChar *key2, const xmlChar *key3,
  643. void *payload, xmlHashDeallocator dealloc) {
  644. return(xmlHashUpdateInternal(hash, key, key2, key3, payload,
  645. dealloc, 1));
  646. }
  647. /**
  648. * xmlHashLookup:
  649. * @hash: hash table
  650. * @key: string key
  651. *
  652. * Find the entry specified by @key.
  653. *
  654. * Returns a pointer to the payload or NULL if no entry was found.
  655. */
  656. void *
  657. xmlHashLookup(xmlHashTablePtr hash, const xmlChar *key) {
  658. return(xmlHashLookup3(hash, key, NULL, NULL));
  659. }
  660. /**
  661. * xmlHashLookup2:
  662. * @hash: hash table
  663. * @key: first string key
  664. * @key2: second string key
  665. *
  666. * Find the payload specified by the (@key, @key2) tuple.
  667. *
  668. * Returns a pointer to the payload or NULL if no entry was found.
  669. */
  670. void *
  671. xmlHashLookup2(xmlHashTablePtr hash, const xmlChar *key,
  672. const xmlChar *key2) {
  673. return(xmlHashLookup3(hash, key, key2, NULL));
  674. }
  675. /**
  676. * xmlHashQLookup:
  677. * @hash: hash table
  678. * @prefix: prefix of the string key
  679. * @name: local name of the string key
  680. *
  681. * Find the payload specified by the QName @prefix:@name or @name.
  682. *
  683. * Returns a pointer to the payload or NULL if no entry was found.
  684. */
  685. void *
  686. xmlHashQLookup(xmlHashTablePtr hash, const xmlChar *prefix,
  687. const xmlChar *name) {
  688. return(xmlHashQLookup3(hash, prefix, name, NULL, NULL, NULL, NULL));
  689. }
  690. /**
  691. * xmlHashQLookup2:
  692. * @hash: hash table
  693. * @prefix: first prefix
  694. * @name: first local name
  695. * @prefix2: second prefix
  696. * @name2: second local name
  697. *
  698. * Find the payload specified by the QNames tuple.
  699. *
  700. * Returns a pointer to the payload or NULL if no entry was found.
  701. */
  702. void *
  703. xmlHashQLookup2(xmlHashTablePtr hash, const xmlChar *prefix,
  704. const xmlChar *name, const xmlChar *prefix2,
  705. const xmlChar *name2) {
  706. return(xmlHashQLookup3(hash, prefix, name, prefix2, name2, NULL, NULL));
  707. }
  708. /**
  709. * xmlHashLookup3:
  710. * @hash: hash table
  711. * @key: first string key
  712. * @key2: second string key
  713. * @key3: third string key
  714. *
  715. * Find the payload specified by the (@key, @key2, @key3) tuple.
  716. *
  717. * Returns a pointer to the payload or NULL if no entry was found.
  718. */
  719. void *
  720. xmlHashLookup3(xmlHashTablePtr hash, const xmlChar *key,
  721. const xmlChar *key2, const xmlChar *key3) {
  722. const xmlHashEntry *entry;
  723. unsigned hashValue;
  724. int found;
  725. if ((hash == NULL) || (hash->size == 0) || (key == NULL))
  726. return(NULL);
  727. hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
  728. entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
  729. if (found)
  730. return(entry->payload);
  731. return(NULL);
  732. }
  733. /**
  734. * xmlHashQLookup3:
  735. * @hash: hash table
  736. * @prefix: first prefix
  737. * @name: first local name
  738. * @prefix2: second prefix
  739. * @name2: second local name
  740. * @prefix3: third prefix
  741. * @name3: third local name
  742. *
  743. * Find the payload specified by the QNames tuple.
  744. *
  745. * Returns a pointer to the payload or NULL if no entry was found.
  746. */
  747. ATTRIBUTE_NO_SANITIZE_INTEGER
  748. void *
  749. xmlHashQLookup3(xmlHashTablePtr hash,
  750. const xmlChar *prefix, const xmlChar *name,
  751. const xmlChar *prefix2, const xmlChar *name2,
  752. const xmlChar *prefix3, const xmlChar *name3) {
  753. const xmlHashEntry *entry;
  754. unsigned hashValue, mask, pos, displ;
  755. if ((hash == NULL) || (hash->size == 0) || (name == NULL))
  756. return(NULL);
  757. hashValue = xmlHashQNameValue(hash->randomSeed, prefix, name, prefix2,
  758. name2, prefix3, name3);
  759. mask = hash->size - 1;
  760. pos = hashValue & mask;
  761. entry = &hash->table[pos];
  762. if (entry->hashValue != 0) {
  763. displ = 0;
  764. hashValue |= MAX_HASH_SIZE;
  765. do {
  766. if ((hashValue == entry->hashValue) &&
  767. (xmlStrQEqual(prefix, name, entry->key)) &&
  768. (xmlStrQEqual(prefix2, name2, entry->key2)) &&
  769. (xmlStrQEqual(prefix3, name3, entry->key3)))
  770. return(entry->payload);
  771. displ++;
  772. pos++;
  773. entry++;
  774. if ((pos & mask) == 0)
  775. entry = hash->table;
  776. } while ((entry->hashValue != 0) &&
  777. (((pos - entry->hashValue) & mask) >= displ));
  778. }
  779. return(NULL);
  780. }
  781. typedef struct {
  782. xmlHashScanner scan;
  783. void *data;
  784. } stubData;
  785. static void
  786. stubHashScannerFull(void *payload, void *data, const xmlChar *key,
  787. const xmlChar *key2 ATTRIBUTE_UNUSED,
  788. const xmlChar *key3 ATTRIBUTE_UNUSED) {
  789. stubData *sdata = (stubData *) data;
  790. sdata->scan(payload, sdata->data, key);
  791. }
  792. /**
  793. * xmlHashScan:
  794. * @hash: hash table
  795. * @scan: scanner function for items in the hash
  796. * @data: extra data passed to @scan
  797. *
  798. * Scan the hash @table and apply @scan to each value.
  799. */
  800. void
  801. xmlHashScan(xmlHashTablePtr hash, xmlHashScanner scan, void *data) {
  802. stubData sdata;
  803. sdata.data = data;
  804. sdata.scan = scan;
  805. xmlHashScanFull(hash, stubHashScannerFull, &sdata);
  806. }
  807. /**
  808. * xmlHashScanFull:
  809. * @hash: hash table
  810. * @scan: scanner function for items in the hash
  811. * @data: extra data passed to @scan
  812. *
  813. * Scan the hash @table and apply @scan to each value.
  814. */
  815. void
  816. xmlHashScanFull(xmlHashTablePtr hash, xmlHashScannerFull scan, void *data) {
  817. const xmlHashEntry *entry, *end;
  818. xmlHashEntry old;
  819. unsigned i;
  820. if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
  821. return;
  822. /*
  823. * We must handle the case that a scanned entry is removed when executing
  824. * the callback (xmlCleanSpecialAttr and possibly other places).
  825. *
  826. * Find the start of a probe sequence to avoid scanning entries twice if
  827. * a deletion happens.
  828. */
  829. entry = hash->table;
  830. end = &hash->table[hash->size];
  831. while (entry->hashValue != 0) {
  832. if (++entry >= end)
  833. entry = hash->table;
  834. }
  835. for (i = 0; i < hash->size; i++) {
  836. if ((entry->hashValue != 0) && (entry->payload != NULL)) {
  837. /*
  838. * Make sure to rescan after a possible deletion.
  839. */
  840. do {
  841. old = *entry;
  842. scan(entry->payload, data, entry->key, entry->key2, entry->key3);
  843. } while ((entry->hashValue != 0) &&
  844. (entry->payload != NULL) &&
  845. ((entry->key != old.key) ||
  846. (entry->key2 != old.key2) ||
  847. (entry->key3 != old.key3)));
  848. }
  849. if (++entry >= end)
  850. entry = hash->table;
  851. }
  852. }
  853. /**
  854. * xmlHashScan3:
  855. * @hash: hash table
  856. * @key: first string key or NULL
  857. * @key2: second string key or NULL
  858. * @key3: third string key or NULL
  859. * @scan: scanner function for items in the hash
  860. * @data: extra data passed to @scan
  861. *
  862. * Scan the hash @table and apply @scan to each value matching
  863. * (@key, @key2, @key3) tuple. If one of the keys is null,
  864. * the comparison is considered to match.
  865. */
  866. void
  867. xmlHashScan3(xmlHashTablePtr hash, const xmlChar *key,
  868. const xmlChar *key2, const xmlChar *key3,
  869. xmlHashScanner scan, void *data) {
  870. stubData sdata;
  871. sdata.data = data;
  872. sdata.scan = scan;
  873. xmlHashScanFull3(hash, key, key2, key3, stubHashScannerFull, &sdata);
  874. }
  875. /**
  876. * xmlHashScanFull3:
  877. * @hash: hash table
  878. * @key: first string key or NULL
  879. * @key2: second string key or NULL
  880. * @key3: third string key or NULL
  881. * @scan: scanner function for items in the hash
  882. * @data: extra data passed to @scan
  883. *
  884. * Scan the hash @table and apply @scan to each value matching
  885. * (@key, @key2, @key3) tuple. If one of the keys is null,
  886. * the comparison is considered to match.
  887. */
  888. void
  889. xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key,
  890. const xmlChar *key2, const xmlChar *key3,
  891. xmlHashScannerFull scan, void *data) {
  892. const xmlHashEntry *entry, *end;
  893. xmlHashEntry old;
  894. unsigned i;
  895. if ((hash == NULL) || (hash->size == 0) || (scan == NULL))
  896. return;
  897. /*
  898. * We must handle the case that a scanned entry is removed when executing
  899. * the callback (xmlCleanSpecialAttr and possibly other places).
  900. *
  901. * Find the start of a probe sequence to avoid scanning entries twice if
  902. * a deletion happens.
  903. */
  904. entry = hash->table;
  905. end = &hash->table[hash->size];
  906. while (entry->hashValue != 0) {
  907. if (++entry >= end)
  908. entry = hash->table;
  909. }
  910. for (i = 0; i < hash->size; i++) {
  911. if ((entry->hashValue != 0) && (entry->payload != NULL)) {
  912. /*
  913. * Make sure to rescan after a possible deletion.
  914. */
  915. do {
  916. if (((key != NULL) && (strcmp((const char *) key,
  917. (const char *) entry->key) != 0)) ||
  918. ((key2 != NULL) && (!xmlFastStrEqual(key2, entry->key2))) ||
  919. ((key3 != NULL) && (!xmlFastStrEqual(key3, entry->key3))))
  920. break;
  921. old = *entry;
  922. scan(entry->payload, data, entry->key, entry->key2, entry->key3);
  923. } while ((entry->hashValue != 0) &&
  924. (entry->payload != NULL) &&
  925. ((entry->key != old.key) ||
  926. (entry->key2 != old.key2) ||
  927. (entry->key3 != old.key3)));
  928. }
  929. if (++entry >= end)
  930. entry = hash->table;
  931. }
  932. }
  933. /**
  934. * xmlHashCopy:
  935. * @hash: hash table
  936. * @copy: copier function for items in the hash
  937. *
  938. * Copy the hash @table using @copy to copy payloads.
  939. *
  940. * Returns the new table or NULL if a memory allocation failed.
  941. */
  942. xmlHashTablePtr
  943. xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) {
  944. const xmlHashEntry *entry, *end;
  945. xmlHashTablePtr ret;
  946. if ((hash == NULL) || (copy == NULL))
  947. return(NULL);
  948. ret = xmlHashCreate(hash->size);
  949. if (ret == NULL)
  950. return(NULL);
  951. if (hash->size == 0)
  952. return(ret);
  953. end = &hash->table[hash->size];
  954. for (entry = hash->table; entry < end; entry++) {
  955. if (entry->hashValue != 0)
  956. xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3,
  957. copy(entry->payload, entry->key));
  958. }
  959. return(ret);
  960. }
  961. /**
  962. * xmlHashSize:
  963. * @hash: hash table
  964. *
  965. * Query the number of elements in the hash table.
  966. *
  967. * Returns the number of elements in the hash table or
  968. * -1 in case of error.
  969. */
  970. int
  971. xmlHashSize(xmlHashTablePtr hash) {
  972. if (hash == NULL)
  973. return(-1);
  974. return(hash->nbElems);
  975. }
  976. /**
  977. * xmlHashRemoveEntry:
  978. * @hash: hash table
  979. * @key: string key
  980. * @dealloc: deallocator function for removed item or NULL
  981. *
  982. * Find the entry specified by the @key and remove it from the hash table.
  983. * Payload will be freed with @dealloc.
  984. *
  985. * Returns 0 on success and -1 if no entry was found.
  986. */
  987. int xmlHashRemoveEntry(xmlHashTablePtr hash, const xmlChar *key,
  988. xmlHashDeallocator dealloc) {
  989. return(xmlHashRemoveEntry3(hash, key, NULL, NULL, dealloc));
  990. }
  991. /**
  992. * xmlHashRemoveEntry2:
  993. * @hash: hash table
  994. * @key: first string key
  995. * @key2: second string key
  996. * @dealloc: deallocator function for removed item or NULL
  997. *
  998. * Remove an entry with two strings as key.
  999. *
  1000. * See xmlHashRemoveEntry.
  1001. *
  1002. * Returns 0 on success and -1 in case of error.
  1003. */
  1004. int
  1005. xmlHashRemoveEntry2(xmlHashTablePtr hash, const xmlChar *key,
  1006. const xmlChar *key2, xmlHashDeallocator dealloc) {
  1007. return(xmlHashRemoveEntry3(hash, key, key2, NULL, dealloc));
  1008. }
  1009. /**
  1010. * xmlHashRemoveEntry3:
  1011. * @hash: hash table
  1012. * @key: first string key
  1013. * @key2: second string key
  1014. * @key3: third string key
  1015. * @dealloc: deallocator function for removed item or NULL
  1016. *
  1017. * Remove an entry with three strings as key.
  1018. *
  1019. * See xmlHashRemoveEntry.
  1020. *
  1021. * Returns 0 on success and -1 in case of error.
  1022. */
  1023. ATTRIBUTE_NO_SANITIZE_INTEGER
  1024. int
  1025. xmlHashRemoveEntry3(xmlHashTablePtr hash, const xmlChar *key,
  1026. const xmlChar *key2, const xmlChar *key3,
  1027. xmlHashDeallocator dealloc) {
  1028. xmlHashEntry *entry, *cur, *next;
  1029. unsigned hashValue, mask, pos, nextpos;
  1030. int found;
  1031. if ((hash == NULL) || (hash->size == 0) || (key == NULL))
  1032. return(-1);
  1033. hashValue = xmlHashValue(hash->randomSeed, key, key2, key3, NULL);
  1034. entry = xmlHashFindEntry(hash, key, key2, key3, hashValue, &found);
  1035. if (!found)
  1036. return(-1);
  1037. if ((dealloc != NULL) && (entry->payload != NULL))
  1038. dealloc(entry->payload, entry->key);
  1039. if (hash->dict == NULL) {
  1040. if (entry->key)
  1041. xmlFree(entry->key);
  1042. if (entry->key2)
  1043. xmlFree(entry->key2);
  1044. if (entry->key3)
  1045. xmlFree(entry->key3);
  1046. }
  1047. /*
  1048. * Find end of probe sequence. Entries at their initial probe
  1049. * position start a new sequence.
  1050. */
  1051. mask = hash->size - 1;
  1052. pos = entry - hash->table;
  1053. cur = entry;
  1054. while (1) {
  1055. nextpos = pos + 1;
  1056. next = cur + 1;
  1057. if ((nextpos & mask) == 0)
  1058. next = hash->table;
  1059. if ((next->hashValue == 0) ||
  1060. (((next->hashValue - nextpos) & mask) == 0))
  1061. break;
  1062. cur = next;
  1063. pos = nextpos;
  1064. }
  1065. /*
  1066. * Backward shift
  1067. */
  1068. next = entry + 1;
  1069. if (cur < entry) {
  1070. xmlHashEntry *end = &hash->table[hash->size];
  1071. memmove(entry, next, (char *) end - (char *) next);
  1072. entry = hash->table;
  1073. end[-1] = *entry;
  1074. next = entry + 1;
  1075. }
  1076. memmove(entry, next, (char *) cur - (char *) entry);
  1077. /*
  1078. * Update entry
  1079. */
  1080. cur->hashValue = 0;
  1081. hash->nbElems--;
  1082. return(0);
  1083. }