|SUCCESS| [v6,7/7] test/hash: add test for key compare functions
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Mon Mar 30 06:28:57 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/162772
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-03-30
commit d8f5a2e9c3
Author: Stephen Hemminger <stephen at networkplumber.org>
hash: move table of hash compare functions out of header
This patch series refactors the hash library's key comparison functions
to reduce code duplication and improve maintainability.
> diff --git a/lib/hash/rte_cuckoo_hash.c b/lib/hash/rte_cuckoo_hash.c
> index 69dcfdfc39..9af02f2abd 100644
> --- a/lib/hash/rte_cuckoo_hash.c
> +++ b/lib/hash/rte_cuckoo_hash.c
[ ... ]
> +/*
> + * All different options to select a key compare function,
> + * based on the key size and custom function.
> + * Not in rte_cuckoo_hash.h to avoid ABI issues.
> + */
> +enum cmp_jump_table_case {
> + KEY_CUSTOM = 0,
> +#if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64)
> + KEY_16_BYTES,
> + KEY_32_BYTES,
> + KEY_48_BYTES,
> + KEY_64_BYTES,
> + KEY_80_BYTES,
> + KEY_96_BYTES,
> + KEY_112_BYTES,
> + KEY_128_BYTES,
> +#endif
> + KEY_OTHER_BYTES,
> + NUM_KEY_CMP_CASES,
> +};
Does this enum definition introduce portability concerns when the same
hash table is accessed from multiple architectures in shared memory?
Since the enum values would differ between x86/ARM64 and other
platforms, can cmp_jump_table_idx remain consistent?
> +/*
> + * Table storing all different key compare functions
> + * (multi-process supported)
> + */
> +static const rte_hash_cmp_eq_t cmp_jump_table[NUM_KEY_CMP_CASES] = {
> + [KEY_CUSTOM] = NULL,
> +#if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64)
> + [KEY_16_BYTES] = rte_hash_k16_cmp_eq,
> + [KEY_32_BYTES] = rte_hash_k32_cmp_eq,
> + [KEY_48_BYTES] = rte_hash_k48_cmp_eq,
> + [KEY_64_BYTES] = rte_hash_k64_cmp_eq,
> + [KEY_80_BYTES] = rte_hash_k80_cmp_eq,
> + [KEY_96_BYTES] = rte_hash_k96_cmp_eq,
> + [KEY_112_BYTES] = rte_hash_k112_cmp_eq,
> + [KEY_128_BYTES] = rte_hash_k128_cmp_eq,
> +#endif
> + [KEY_OTHER_BYTES] = memcmp,
> +};
[ ... ]
> @@ -406,13 +462,13 @@ rte_hash_create(const struct rte_hash_parameters *params)
> goto err_unlock;
> }
>
> -/*
> - * If x86 architecture is used, select appropriate compare function,
> - * which may use x86 intrinsics, otherwise use memcmp
> - */
> -#if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64)
> /* Select function to compare keys */
> switch (params->key_len) {
> +#if defined(RTE_ARCH_X86) || defined(RTE_ARCH_ARM64)
> + /*
> + * If x86 architecture is used, select appropriate compare function,
> + * which may use x86 intrinsics, otherwise use memcmp
> + */
> case 16:
> h->cmp_jump_table_idx = KEY_16_BYTES;
> break;
[ ... ]
> diff --git a/lib/hash/rte_cuckoo_hash.h b/lib/hash/rte_cuckoo_hash.h
> index cf5cd29144..5b3c51348e 100644
> --- a/lib/hash/rte_cuckoo_hash.h
> +++ b/lib/hash/rte_cuckoo_hash.h
[ ... ]
> @@ -189,7 +112,7 @@ struct __rte_cache_aligned rte_hash {
> uint32_t hash_func_init_val; /**< Init value used by hash_func. */
> rte_hash_cmp_eq_t rte_hash_custom_cmp_eq;
> /**< Custom function used to compare keys. */
> - enum cmp_jump_table_case cmp_jump_table_idx;
> + unsigned int cmp_jump_table_idx;
> /**< Indicates which compare function to use. */
> unsigned int sig_cmp_fn;
> /**< Indicates which signature compare function to use. */
commit 1f3b8d4a2e
Author: Stephen Hemminger <stephen at networkplumber.org>
hash: use static_assert
[ ... ]
commit 5e9c7f2d3a
Author: Stephen Hemminger <stephen at networkplumber.org>
hash: remove spurious warnings in CRC32 init
[ ... ]
commit 4a8e6b9f1c
Author: Stephen Hemminger <stephen at networkplumber.org>
hash: simplify key comparison across architectures
> diff --git a/lib/hash/rte_cmp_arm64.h b/lib/hash/rte_cmp_arm64.h
> index a3e85635eb..f209aaf474 100644
> --- a/lib/hash/rte_cmp_arm64.h
> +++ b/lib/hash/rte_cmp_arm64.h
[ ... ]
> static inline int
> -rte_hash_k32_cmp_eq(const void *key1, const void *key2, size_t key_len)
> +rte_hash_k32_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> {
> - return rte_hash_k16_cmp_eq(key1, key2, key_len) ||
> - rte_hash_k16_cmp_eq((const char *) key1 + 16,
> - (const char *) key2 + 16, key_len);
Does the switch from logical OR to bitwise OR change the short-circuit
behavior in a way that affects performance?
> + return rte_hash_k16_cmp_eq(key1, key2, 16) |
> + rte_hash_k16_cmp_eq((const uint8_t *)key1 + 16,
> + (const uint8_t *)key2 + 16, 16);
> }
[ ... ]
> diff --git a/lib/hash/rte_cmp_generic.h b/lib/hash/rte_cmp_generic.h
> new file mode 100644
> index 0000000000..771180e97a
> --- /dev/null
> +++ b/lib/hash/rte_cmp_generic.h
[ ... ]
> +/* Function to compare 16 byte keys */
> +static inline int
> +rte_hash_k16_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> +{
> +#ifdef RTE_ARCH_64
> + const unaligned_uint64_t *k1 = key1;
> + const unaligned_uint64_t *k2 = key2;
> +
> + return !!((k1[0] ^ k2[0]) | (k1[1] ^ k2[1]));
> +#else
> + const unaligned_uint32_t *k1 = key1;
> + const unaligned_uint32_t *k2 = key2;
> +
> + return !!((k1[0] ^ k2[0]) | (k1[1] ^ k2[1]) |
> + (k1[2] ^ k2[2]) | (k1[3] ^ k2[3]));
> +#endif
> +}
[ ... ]
> diff --git a/lib/hash/rte_cuckoo_hash.c b/lib/hash/rte_cuckoo_hash.c
> index 9af02f2abd..5bbc3c5464 100644
> --- a/lib/hash/rte_cuckoo_hash.c
> +++ b/lib/hash/rte_cuckoo_hash.c
[ ... ]
> +static int
> +rte_hash_k48_cmp_eq(const void *key1, const void *key2, size_t key_len)
> +{
> + return rte_hash_k16_cmp_eq(key1, key2, key_len) |
> + rte_hash_k16_cmp_eq((const uint8_t *) key1 + 16,
> + (const uint8_t *) key2 + 16, key_len) |
> + rte_hash_k16_cmp_eq((const uint8_t *) key1 + 32,
> + (const uint8_t *) key2 + 32, key_len);
> +}
Does this code leak the key_len parameter? rte_hash_k48_cmp_eq is
called with a fixed key_len=48, but passes this value to
rte_hash_k16_cmp_eq which expects key_len=16 (or marks it __rte_unused).
[ ... ]
commit a9f2c5e8d1
Author: Stephen Hemminger <stephen at networkplumber.org>
hash: add support for common small key sizes
> diff --git a/lib/hash/rte_cuckoo_hash.c b/lib/hash/rte_cuckoo_hash.c
> index 5bbc3c5464..5d495ae973 100644
> --- a/lib/hash/rte_cuckoo_hash.c
> +++ b/lib/hash/rte_cuckoo_hash.c
[ ... ]
> +static inline int
> +rte_hash_k2_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> +{
> + const unaligned_uint16_t *k1 = key1;
> + const unaligned_uint16_t *k2 = key2;
> +
> + return !!(k1[0] ^ k2[0]);
> +}
[ ... ]
> +static int
> +rte_hash_k3_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> +{
> + return rte_hash_k2_cmp_eq(key1, key2, 2)
> + | (((const uint8_t *)key1)[2] ^ ((const uint8_t *)key2)[2]);
> +}
Does this code pass the wrong key_len to rte_hash_k2_cmp_eq? The
function is marked __rte_unused, but the call passes 2 explicitly.
[ ... ]
> +static inline int
> +rte_hash_k64_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> {
> - return rte_hash_k32_cmp_eq(key1, key2, key_len) |
> - rte_hash_k32_cmp_eq((const uint8_t *) key1 + 32,
> - (const uint8_t *) key2 + 32, key_len);
> + return rte_hash_k32_cmp_eq(key1, key2, 32) |
> + rte_hash_k32_cmp_eq((const uint8_t *)key1 + 32,
> + (const uint8_t *)key2 + 32, 32);
> }
>
> static int
> -rte_hash_k80_cmp_eq(const void *key1, const void *key2, size_t key_len)
> +rte_hash_k80_cmp_eq(const void *key1, const void *key2, size_t key_len __rte_unused)
> {
> - return rte_hash_k64_cmp_eq(key1, key2, key_len) |
> - rte_hash_k16_cmp_eq((const uint8_t *) key1 + 64,
> - (const uint8_t *) key2 + 64, key_len);
> + return rte_hash_k64_cmp_eq(key1, key2, 64) |
> + rte_hash_k16_cmp_eq((const uint8_t *)key1 + 64,
> + (const uint8_t *)key2 + 64, 64);
Does this code overflow the key buffer? rte_hash_k80_cmp_eq is called
for 80-byte keys, but compares 64+16 bytes and then passes key_len=64 to
rte_hash_k16_cmp_eq when it should be 16.
[ ... ]
More information about the test-report
mailing list