diff --git a/crypto/hashtable/hashtable.c b/crypto/hashtable/hashtable.c index af800c2cdf..065174feb7 100644 --- a/crypto/hashtable/hashtable.c +++ b/crypto/hashtable/hashtable.c @@ -700,12 +700,15 @@ int ossl_ht_insert(HT *h, HT_KEY *key, HT_VALUE *data, HT_VALUE **olddata) if (newval == NULL) goto out; + if (key->cached_hash) + hash = key->cached_hash; + else + hash = key->cached_hash = newval->value.key.cached_hash = h->config.ht_hash_fn(key); + /* * we have to take our lock here to prevent other changes * to the bucket list */ - hash = h->config.ht_hash_fn(key); - for (i = 0; (rc = ossl_ht_insert_locked(h, hash, newval, olddata)) == -1 && i < 4; @@ -733,7 +736,10 @@ HT_VALUE *ossl_ht_get(HT *h, HT_KEY *key) uint64_t ehash; int lockless_reads = h->config.lockless_reads; - hash = h->config.ht_hash_fn(key); + if (key->cached_hash) + hash = key->cached_hash; + else + hash = key->cached_hash = h->config.ht_hash_fn(key); if (!h->config.no_rcu) md = ossl_rcu_deref(&h->md); @@ -792,7 +798,10 @@ int ossl_ht_delete(HT *h, HT_KEY *key) if (h->config.lockless_reads) return 0; - hash = h->config.ht_hash_fn(key); + if (key->cached_hash) + hash = key->cached_hash; + else + hash = key->cached_hash = h->config.ht_hash_fn(key); neigh_idx = hash & h->md->neighborhood_mask; PREFETCH_NEIGHBORHOOD(h->md->neighborhoods[neigh_idx]); diff --git a/include/internal/hashtable.h b/include/internal/hashtable.h index 04b2d7f20a..320408bba4 100644 --- a/include/internal/hashtable.h +++ b/include/internal/hashtable.h @@ -23,6 +23,7 @@ typedef struct ht_internal_st HT; * Represents a key to a hashtable */ typedef struct ht_key_header_st { + uint64_t cached_hash; size_t keysize; size_t bufsize; uint8_t *keybuf; @@ -169,10 +170,22 @@ static ossl_inline ossl_unused int ossl_key_raw_copy(HT_KEY *key, const uint8_t (key)->keysize += tmplen; \ } while (0) +#define HT_INIT_KEY_CACHED(key, hash) \ + do { \ + HT_INIT_KEY((key)); \ + (key)->key_header.cached_hash = hash; \ + } while (0) + +#define HT_KEY_GET_HASH(key) (key)->key_header.cached_hash + /* * Resets a hash table key to a known state */ -#define HT_KEY_RESET(key) memset((key)->key_header.keybuf, 0, (key)->key_header.keysize) +#define HT_KEY_RESET(key) \ + do { \ + memset((key)->key_header.keybuf, 0, (key)->key_header.keysize); \ + (key)->key_header.cached_hash = 0; \ + } while (0) /* * Sets a scalar field in a hash table key diff --git a/test/lhash_test.c b/test/lhash_test.c index 9f44ffb7ca..eae2999f4e 100644 --- a/test/lhash_test.c +++ b/test/lhash_test.c @@ -244,6 +244,7 @@ static int test_int_hashtable(int idx) /* insert */ HT_INIT_KEY(&key); for (i = 0; i < n_int_tests; i++) { + HT_KEY_RESET(&key); HT_SET_KEY_FIELD(&key, mykey, int_tests[i]); if (!TEST_int_eq(ossl_ht_test_int_insert(ht, TO_HT_KEY(&key), &int_tests[i], NULL), @@ -280,6 +281,7 @@ static int test_int_hashtable(int idx) /* delete */ for (i = 0; i < n_dels; i++) { + HT_KEY_RESET(&key); HT_SET_KEY_FIELD(&key, mykey, dels[i].data); todel = ossl_ht_delete(ht, TO_HT_KEY(&key)); if (dels[i].should_del) { @@ -439,6 +441,7 @@ static int test_hashtable_stress(int idx) goto end; } *p = 3 * i + 1; + HT_KEY_RESET(&key); HT_SET_KEY_FIELD(&key, mykey, *p); if (!TEST_int_eq(ossl_ht_test_int_insert(h, TO_HT_KEY(&key), p, NULL), @@ -455,6 +458,7 @@ static int test_hashtable_stress(int idx) /* delete or get in a different order */ for (i = 0; i < n; i++) { const int j = (7 * i + 4) % n * 3 + 1; + HT_KEY_RESET(&key); HT_SET_KEY_FIELD(&key, mykey, j); switch (idx % 2) {