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