+/* Internal routine to find entry in the hash table by `key' and `context'.
+ Returns the previous entry (if exists) as well to `prev_entry'. */
+
+static inline SilcHashTableEntry *
+silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
+ void *context,
+ SilcHashTableEntry *prev_entry,
+ SilcHashFunction hash,
+ void *hash_user_context,
+ SilcHashCompare compare,
+ void *compare_user_context)
+{
+ SilcHashTableEntry *entry, prev = NULL;
+ SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+ SILC_HT_DEBUG(("index %d key %p context %p", i, key, context));
+
+ entry = &ht->table[i];
+ if (ht->compare) {
+ while (*entry) {
+ if (compare((*entry)->key, key, compare_user_context) &&
+ (*entry)->context == context)
+ break;
+ prev = *entry;
+ entry = &(*entry)->next;
+ }
+ } else {
+ while (*entry) {
+ if ((*entry)->key == key && (*entry)->context == context)
+ break;
+ prev = *entry;
+ entry = &(*entry)->next;
+ }
+ }
+
+ if (prev_entry)
+ *prev_entry = prev;
+ return entry;
+}
+
+/* Internal routine to find entry in the hash table by `key'. */
+
+static inline SilcHashTableEntry *
+silc_hash_table_find_internal_simple(SilcHashTable ht, void *key,
+ SilcHashFunction hash,
+ void *hash_user_context,
+ SilcHashCompare compare,
+ void *compare_user_context)
+{
+ SilcHashTableEntry *entry;
+ SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+ SILC_HT_DEBUG(("index %d key %p", i, key));
+
+ entry = &ht->table[i];
+ if (compare) {
+ while (*entry && !compare((*entry)->key, key, compare_user_context))
+ entry = &(*entry)->next;
+ } else {
+ while (*entry && (*entry)->key != key)
+ entry = &(*entry)->next;
+ }
+
+ return entry;
+}
+
+/* Internal routine to find all keys by `key'. This may return multiple
+ entries if multiple entries with same key exists. With specific
+ hash and comparison functions. */
+
+static inline void
+silc_hash_table_find_internal_all(SilcHashTable ht, void *key,
+ SilcHashFunction hash,
+ void *hash_user_context,
+ SilcHashCompare compare,
+ void *compare_user_context,
+ SilcHashForeach foreach,
+ void *foreach_user_context)
+{
+ SilcHashTableEntry e, tmp;
+ SilcBool auto_rehash, found = FALSE;
+ SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+ SILC_HT_DEBUG(("index %d key %p", i, key));
+
+ /* Disallow auto rehashing while going through the table since we call
+ the `foreach' function which could alter the table. */
+ auto_rehash = ht->auto_rehash;
+ ht->auto_rehash = FALSE;
+
+ e = ht->table[i];
+ if (compare) {
+ while (e) {
+ tmp = e->next;
+ if (compare(e->key, key, compare_user_context)) {
+ found = TRUE;
+ foreach(e->key, e->context, foreach_user_context);
+ }
+ e = tmp;
+ }
+ } else {
+ while (e) {
+ tmp = e->next;
+ if (e->key == key) {
+ found = TRUE;
+ foreach(e->key, e->context, foreach_user_context);
+ }
+ e = tmp;
+ }
+ }
+
+ /* If nothing was found call with NULL context the callback */
+ if (!found)
+ foreach(key, NULL, foreach_user_context);
+
+ ht->auto_rehash = auto_rehash;
+}
+
+/* Internal routine to add new key to the hash table */
+
+static inline SilcBool
+silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
+ SilcHashFunction hash,
+ void *hash_user_context)
+{
+ SilcHashTableEntry *entry;
+ SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+ SILC_HT_DEBUG(("index %d key %p", i, key));
+
+ entry = &ht->table[i];
+ if (*entry) {
+ /* The entry exists already. We have a collision, add it to the
+ list to avoid collision. */
+ SilcHashTableEntry e, tmp;
+
+ e = *entry;
+ tmp = e->next;
+ while (tmp) {
+ e = tmp;
+ tmp = tmp->next;
+ }
+
+ SILC_HT_DEBUG(("Collision; adding new key to list"));
+
+ e->next = silc_scalloc(ht->stack, 1, sizeof(*e->next));
+ if (!e->next)
+ return FALSE;
+ e->next->key = key;
+ e->next->context = context;
+ ht->entry_count++;
+ } else {
+ /* New key */
+ SILC_HT_DEBUG(("New key"));
+ *entry = silc_scalloc(ht->stack, 1, sizeof(**entry));
+ if (!(*entry))
+ return FALSE;
+ (*entry)->key = key;
+ (*entry)->context = context;
+ ht->entry_count++;
+ }
+
+ if (SILC_HASH_REHASH_INC)
+ silc_hash_table_rehash(ht, 0);
+
+ return TRUE;
+}
+
+/* Internal routine to replace old key with new one (if it exists) */
+
+static inline SilcBool
+silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
+ SilcHashFunction hash,
+ void *hash_user_context)
+{
+ SilcHashTableEntry *entry;
+ SilcUInt32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+ SILC_HT_DEBUG(("index %d key %p", i, key));
+
+ entry = &ht->table[i];
+ if (*entry) {
+ /* The entry exists already. We have a collision, replace the old
+ key and context. */
+ if (ht->destructor)
+ ht->destructor((*entry)->key, (*entry)->context,
+ ht->destructor_user_context);
+ } else {
+ /* New key */
+ *entry = silc_scalloc(ht->stack, 1, sizeof(**entry));
+ if (!(*entry))
+ return FALSE;
+ ht->entry_count++;
+ }
+
+ (*entry)->key = key;
+ (*entry)->context = context;
+
+ if (SILC_HASH_REHASH_INC)
+ silc_hash_table_rehash(ht, 0);
+
+ return TRUE;
+}
+