Please ignore this patch, it contains a silly mistake on hash.h 
which modifies its comments causing a typo.

sorry.

> 在 2015年11月26日,上午10:20,Peng <xnhp0...@icloud.com> 写道:
> 
> Signed-off-by: Peng <xnhp0...@icloud.com>
> ---
> helper/Makefile.am                      |    2 +
> helper/cuckoo_hash.c                    | 1117 +++++++++++++++++++++++++++++++
> helper/include/odp/helper/cuckoo_hash.h |  435 ++++++++++++
> helper/include/odp/helper/ring.h        |   43 ++
> helper/ring.c                           |   28 +
> helper/test/Makefile.am                 |    4 +-
> helper/test/odp_cuckoo_hash.c           |  779 +++++++++++++++++++++
> include/odp/api/hash.h                  |   16 +-
> 8 files changed, 2422 insertions(+), 2 deletions(-)
> create mode 100644 helper/cuckoo_hash.c
> create mode 100644 helper/include/odp/helper/cuckoo_hash.h
> create mode 100644 helper/test/odp_cuckoo_hash.c
> 
> diff --git a/helper/Makefile.am b/helper/Makefile.am
> index e72507e..e923aa8 100644
> --- a/helper/Makefile.am
> +++ b/helper/Makefile.am
> @@ -18,6 +18,7 @@ helperinclude_HEADERS = \
>                 $(srcdir)/include/odp/helper/ipsec.h\
>                 $(srcdir)/include/odp/helper/tcp.h\
>                 $(srcdir)/include/odp/helper/table.h\
> +               $(srcdir)/include/odp/helper/cuckoo_hash.h\
>                 $(srcdir)/include/odp/helper/udp.h
> 
> noinst_HEADERS = \
> @@ -30,6 +31,7 @@ noinst_HEADERS = \
> __LIB__libodphelper_la_SOURCES = \
>                                       linux.c \
>                                       ring.c \
> +                                     cuckoo_hash.c \
>                                       hashtable.c \
>                                       lineartable.c
> 
> diff --git a/helper/cuckoo_hash.c b/helper/cuckoo_hash.c
> new file mode 100644
> index 0000000..4a4b56b
> --- /dev/null
> +++ b/helper/cuckoo_hash.c
> @@ -0,0 +1,1117 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <string.h>
> +#include <stdint.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdarg.h>
> +#include <sys/queue.h>
> +
> +#include <odp/helper/cuckoo_hash.h>
> +#include <odp/shared_memory.h>
> +#include <odp/spinlock.h>
> +#include <odp/align.h>
> +#include <odp/api/errno.h>
> +#include <odp/rwlock.h>
> +#include <odp/hints.h>
> +#include <odp/helper/ring.h>
> +#include "odph_debug.h"
> +
> +/* Macro to enable/disable run-time checking of function parameters */
> +#define RETURN_IF_TRUE(cond, retval) do { \
> +     if (cond) \
> +             return retval; \
> +} while (0)
> +
> +/* Hash function used if none is specified */
> +#include <odp/hash.h>
> +
> +/** Number of items per bucket. */
> +#define HASH_BUCKET_ENTRIES          4
> +#define NULL_SIGNATURE                       0
> +#define KEY_ALIGNMENT                        16
> +
> +/** Maximum size of hash table that can be created. */
> +#define HASH_ENTRIES_MAX        1048576
> +
> +/** Maximum number of keys that can be
> + *  searched for using odph_hash_lookup_bulk. */
> +#define HASH_LOOKUP_BULK_MAX 64
> +#define HASH_LOOKUP_MULTI_MAX        HASH_LOOKUP_BULK_MAX
> +
> +/* Structure storing both primary and secondary hashes */
> +struct cuckoo_hash_signatures {
> +     union {
> +             struct {
> +                     uint32_t current;
> +                     uint32_t alt;
> +             };
> +             uint64_t sig;
> +     };
> +};
> +
> +/* Structure that stores key-value pair */
> +struct cuckoo_hash_key {
> +     union {
> +             uintptr_t idata;
> +             void *pdata;
> +     };
> +     /* Variable key size */
> +     char key[0];
> +} ODP_ALIGNED(KEY_ALIGNMENT);
> +
> +/** Bucket structure */
> +struct cuckoo_hash_bucket {
> +     struct cuckoo_hash_signatures signatures[HASH_BUCKET_ENTRIES];
> +     /* Includes dummy key index that always contains index 0 */
> +     uint32_t key_idx[HASH_BUCKET_ENTRIES + 1];
> +     uint8_t flag[HASH_BUCKET_ENTRIES];
> +} ODP_ALIGNED_CACHE;
> +
> +/**
> + * Aligns input parameter to the next power of 2
> + *
> + * @param x
> + *   The integer value to algin
> + *
> + * @return
> + *   Input parameter aligned to the next power of 2
> + */
> +static inline uint32_t
> +align32pow2(uint32_t x)
> +{
> +     x--;
> +     x |= x >> 1;
> +     x |= x >> 2;
> +     x |= x >> 4;
> +     x |= x >> 8;
> +     x |= x >> 16;
> +
> +     return x + 1;
> +}
> +
> +/**
> + * Returns true if n is a power of 2
> + * @param n
> + *     Number to check
> + * @return 1 if true, 0 otherwise
> + */
> +static inline int
> +is_power_of_2(uint32_t n)
> +{
> +     return n && !(n & (n - 1));
> +}
> +
> +odph_cuckoo_hash_tbl_t *
> +odph_cuckoo_hash_find_existing(const char *name)
> +{
> +     odph_cuckoo_hash_tbl_t *h = NULL;
> +     odp_shm_t shm = odp_shm_lookup(name);
> +
> +     if (shm == ODP_SHM_INVALID)
> +             return NULL;
> +
> +     h = (odph_cuckoo_hash_tbl_t *)odp_shm_addr(shm);
> +     return h;
> +}
> +
> +int
> +odph_cuckoo_hash_create(odph_cuckoo_hash_tbl_t **ht,
> +                     const odph_cuckoo_hash_param_t *params)
> +{
> +     odph_cuckoo_hash_tbl_t *h;
> +     odph_ring_t *r = NULL;
> +     odp_shm_t shm_h, shm_b, shm_k;
> +
> +     char bucket_name[ODPH_CUCKOO_HASH_NAMESIZE + 2],
> +          key_name[ODPH_CUCKOO_HASH_NAMESIZE + 2];
> +
> +     void *ptr, *k = NULL;
> +     void *buckets = NULL;
> +     char ring_name[ODPH_RING_NAMESIZE];
> +     unsigned i;
> +
> +     if (params == NULL) {
> +             ODPH_ERR("no parameters\n");
> +             *ht = NULL;
> +             return -EINVAL;
> +     }
> +
> +     /* Check for valid parameters */
> +     if (
> +         (params->entries > HASH_ENTRIES_MAX) ||
> +         (params->entries < HASH_BUCKET_ENTRIES) ||
> +         (params->key_len == 0) ||
> +         (strlen(params->name) == 0)) {
> +             ODPH_ERR("invalid parameters\n");
> +             *ht = NULL;
> +             return -EINVAL;
> +     }
> +
> +     /* Guarantee there's no existing */
> +     h = odph_cuckoo_hash_find_existing(params->name);
> +     if (h != NULL) {
> +             ODPH_ERR("cuckoo hash table %s already exists\n", params->name);
> +             *ht = h;
> +             return -EEXIST;
> +     }
> +
> +     shm_h = odp_shm_reserve(
> +                             params->name, sizeof(odph_cuckoo_hash_tbl_t),
> +                             ODP_CACHE_LINE_SIZE, ODP_SHM_SW_ONLY);
> +
> +     if (shm_h == ODP_SHM_INVALID) {
> +             ODPH_ERR("shm allocation failed for odph_cuckoo_hash_tbl %s\n",
> +                      params->name);
> +             *ht = NULL;
> +             return -ENOMEM;
> +     }
> +     h = odp_shm_addr(shm_h);
> +
> +     snprintf(bucket_name, sizeof(bucket_name), "B_%s", params->name);
> +     const uint32_t num_buckets =
> +             align32pow2(params->entries) / HASH_BUCKET_ENTRIES;
> +
> +     shm_b = odp_shm_reserve(
> +                             bucket_name,
> +                             num_buckets * sizeof(struct cuckoo_hash_bucket),
> +                             ODP_CACHE_LINE_SIZE, ODP_SHM_SW_ONLY);
> +
> +     if (shm_b == ODP_SHM_INVALID) {
> +             ODPH_ERR("shm allocation failed for cuckoo_hash_bucket\n");
> +             odp_shm_free(shm_h);
> +             *ht = NULL;
> +             return -ENOMEM;
> +     }
> +
> +     buckets = odp_shm_addr(shm_b);
> +
> +     snprintf(key_name, sizeof(key_name), "K_%s", params->name);
> +     const uint32_t key_entry_size =
> +             sizeof(struct cuckoo_hash_key) + params->key_len;
> +     /* Store all keys and leave the first entry
> +        as a dummy entry for lookup_bulk */
> +     const uint64_t key_tbl_size = key_entry_size * (params->entries + 1);
> +
> +     shm_k = odp_shm_reserve(key_name, key_tbl_size, ODP_CACHE_LINE_SIZE,
> +                             ODP_SHM_SW_ONLY);
> +     if (shm_k == ODP_SHM_INVALID) {
> +             ODPH_ERR("shm allocation failed for key_store\n");
> +             odp_shm_free(shm_b);
> +             odp_shm_free(shm_h);
> +             *ht = NULL;
> +             return -ENOMEM;
> +     }
> +     k = odp_shm_addr(shm_k);
> +
> +     snprintf(ring_name, sizeof(ring_name), "RI_%s", params->name);
> +     r = odph_ring_lookup(ring_name);
> +     if (r != NULL) {
> +             /* clear the free ring */
> +             while (odph_ring_sc_dequeue(r, ptr) == 0) {
> +                     /* empty */
> +             }
> +     } else {
> +             r = odph_ring_create(ring_name,
> +                                  align32pow2(params->entries + 1), 0);
> +     }
> +
> +     if (r == NULL) {
> +             odp_shm_free(shm_k);
> +             odp_shm_free(shm_b);
> +             odp_shm_free(shm_h);
> +             *ht = NULL;
> +             return -ENOMEM;
> +     }
> +
> +     /* Setup hash context */
> +     snprintf(h->name, sizeof(h->name), "%s", params->name);
> +     h->entries = params->entries;
> +     h->key_len = params->key_len;
> +     h->key_entry_size = key_entry_size;
> +     h->hash_func_init_val = params->hash_func_init_val;
> +     h->hash_cmp_eq = memcmp;
> +
> +     h->num_buckets = num_buckets;
> +     h->bucket_bitmask = h->num_buckets - 1;
> +     h->buckets = buckets;
> +
> +     /*default hash function is CRC 32*/
> +     h->hash_func = (params->hash_func == NULL) ?
> +             odp_hash_crc32c : params->hash_func;
> +
> +     h->key_store = k;
> +     h->free_slots = r;
> +
> +     h->shm_bucket = shm_b;
> +     h->shm_key = shm_k;
> +
> +     /* populate the free slots ring.
> +      * Entry zero is reserved for key misses
> +      */
> +     for (i = 1; i < params->entries + 1; i++)
> +             odph_ring_sp_enqueue(r, (void *)((uintptr_t)i));
> +     *ht = h;
> +     return 0;
> +}
> +
> +void
> +odph_cuckoo_hash_destroy(odph_cuckoo_hash_tbl_t *h)
> +{
> +     if (h == NULL)
> +             return;
> +
> +     odph_ring_free(h->free_slots);
> +     odp_shm_free(h->shm_key);
> +     odp_shm_free(h->shm_bucket);
> +     odp_shm_free(odp_shm_lookup(h->name));
> +}
> +
> +static uint32_t hash(const odph_cuckoo_hash_tbl_t *h, const void *key)
> +{
> +     /* calc hash result by key */
> +     return h->hash_func(key, h->key_len, h->hash_func_init_val);
> +}
> +
> +/* Calc the secondary hash value from the primary hash value of a given key 
> */
> +static inline uint32_t
> +hash_secondary(const uint32_t primary_hash)
> +{
> +     static const unsigned all_bits_shift = 12;
> +     static const unsigned alt_bits_xor = 0x5bd1e995;
> +
> +     uint32_t tag = primary_hash >> all_bits_shift;
> +
> +     return (primary_hash ^ ((tag + 1) * alt_bits_xor));
> +}
> +
> +void
> +odph_cuckoo_hash_reset(odph_cuckoo_hash_tbl_t *h)
> +{
> +     void *ptr;
> +     unsigned i;
> +
> +     if (h == NULL)
> +             return;
> +
> +     memset(h->buckets, 0,
> +            h->num_buckets * sizeof(struct cuckoo_hash_bucket));
> +     memset(h->key_store, 0, h->key_entry_size * (h->entries + 1));
> +
> +     /* clear the free ring */
> +     while (odph_ring_sc_dequeue(h->free_slots, ptr) == 0) {
> +             /* empty */
> +     }
> +
> +     /* Repopulate the free slots ring.
> +      * Entry zero is reserved for key misses */
> +     for (i = 1; i < h->entries + 1; i++)
> +             odph_ring_sp_enqueue(h->free_slots, (void *)((uintptr_t)i));
> +}
> +
> +/* Search for an entry that can be pushed to its alternative location */
> +static inline int
> +make_space_bucket(const odph_cuckoo_hash_tbl_t *h,
> +               struct cuckoo_hash_bucket *bkt)
> +{
> +     unsigned i, j;
> +     int ret;
> +     uint32_t next_bucket_idx;
> +     struct cuckoo_hash_bucket *next_bkt[HASH_BUCKET_ENTRIES];
> +
> +     /*
> +      * Push existing item (search for bucket with space in
> +      * alternative locations) to its alternative location
> +      */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             /* Search for space in alternative locations */
> +             next_bucket_idx = bkt->signatures[i].alt & h->bucket_bitmask;
> +             next_bkt[i] = &h->buckets[next_bucket_idx];
> +             for (j = 0; j < HASH_BUCKET_ENTRIES; j++) {
> +                     if (next_bkt[i]->signatures[j].sig == NULL_SIGNATURE)
> +                             break;
> +             }
> +
> +             if (j != HASH_BUCKET_ENTRIES)
> +                     break;
> +     }
> +
> +     /* Alternative location has spare room (end of recursive function) */
> +     if (i != HASH_BUCKET_ENTRIES) {
> +             next_bkt[i]->signatures[j].alt = bkt->signatures[i].current;
> +             next_bkt[i]->signatures[j].current = bkt->signatures[i].alt;
> +             next_bkt[i]->key_idx[j] = bkt->key_idx[i];
> +             return i;
> +     }
> +
> +     /* Pick entry that has not been pushed yet */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++)
> +             if (bkt->flag[i] == 0)
> +                     break;
> +
> +     /* All entries have been pushed, so entry cannot be added */
> +     if (i == HASH_BUCKET_ENTRIES)
> +             return -ENOSPC;
> +
> +     /* Set flag to indicate that this entry is going to be pushed */
> +     bkt->flag[i] = 1;
> +     /* Need room in alternative bucket to insert the pushed entry */
> +     ret = make_space_bucket(h, next_bkt[i]);
> +     /*
> +      * After recursive function.
> +      * Clear flags and insert the pushed entry
> +      * in its alternative location if successful,
> +      * or return error
> +      */
> +     bkt->flag[i] = 0;
> +     if (ret >= 0) {
> +             next_bkt[i]->signatures[ret].alt = bkt->signatures[i].current;
> +             next_bkt[i]->signatures[ret].current = bkt->signatures[i].alt;
> +             next_bkt[i]->key_idx[ret] = bkt->key_idx[i];
> +             return i;
> +     }
> +
> +     return ret;
> +}
> +
> +static inline int32_t
> +cuckoo_hash_add_key_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h, const void *key,
> +             uint32_t sig, void *data)
> +{
> +     uint32_t alt_hash;
> +     uint32_t prim_bucket_idx, sec_bucket_idx;
> +     unsigned i;
> +     struct cuckoo_hash_bucket *prim_bkt, *sec_bkt;
> +     struct cuckoo_hash_key *new_k, *k, *keys = h->key_store;
> +     void *slot_id;
> +     uint32_t new_idx;
> +     int ret;
> +
> +     prim_bucket_idx = sig & h->bucket_bitmask;
> +     prim_bkt = &h->buckets[prim_bucket_idx];
> +     __builtin_prefetch((const void *)(uintptr_t)prim_bkt, 0, 3);
> +
> +     alt_hash = hash_secondary(sig);
> +     sec_bucket_idx = alt_hash & h->bucket_bitmask;
> +     sec_bkt = &h->buckets[sec_bucket_idx];
> +     __builtin_prefetch((const void *)(uintptr_t)sec_bkt, 0, 3);
> +
> +     /* Get a new slot for storing the new key */
> +     if (odph_ring_sc_dequeue_bulk(h->free_slots, &slot_id, 1) != 0)
> +             return -ENOSPC;
> +     new_k = (void *)((uintptr_t)(keys)
> +                     + ((uintptr_t)slot_id * h->key_entry_size));
> +     __builtin_prefetch((const void *)(uintptr_t)new_k, 0, 3);
> +     new_idx = (uint32_t)((uintptr_t)slot_id);
> +
> +     /* Check if key is already inseodpd in primary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     prim_bkt->signatures[i].current == sig &&
> +                     prim_bkt->signatures[i].alt == alt_hash)  {
> +                     k = (struct cuckoo_hash_key *)((char *)keys +
> +                             prim_bkt->key_idx[i] * h->key_entry_size);
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             odph_ring_sp_enqueue(h->free_slots, slot_id);
> +                             /* Update data */
> +                             k->pdata = data;
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (prim_bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     /* Check if key is already inseodpd in secondary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     sec_bkt->signatures[i].alt == sig &&
> +                     sec_bkt->signatures[i].current == alt_hash)  {
> +                     k = (struct cuckoo_hash_key *)((char *)keys +
> +                             sec_bkt->key_idx[i] * h->key_entry_size);
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             odph_ring_sp_enqueue(h->free_slots, slot_id);
> +                             /* Update data */
> +                             k->pdata = data;
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (sec_bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     /* Copy key */
> +     memcpy(new_k->key, key, h->key_len);
> +     new_k->pdata = data;
> +
> +     /* Insert new entry is there is room in the primary bucket */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             /* Check if slot is available */
> +             if (odp_likely(prim_bkt->signatures[i].sig == NULL_SIGNATURE)) {
> +                     prim_bkt->signatures[i].current = sig;
> +                     prim_bkt->signatures[i].alt = alt_hash;
> +                     prim_bkt->key_idx[i] = new_idx;
> +                     return new_idx - 1;
> +             }
> +     }
> +
> +     /* Primary bucket is full, so we need to make space for new entry */
> +     ret = make_space_bucket(h, prim_bkt);
> +     /*
> +      * After recursive function.
> +      * Insert the new entry in the position of the pushed entry
> +      * if successful or return error and
> +      * store the new slot back in the ring
> +      */
> +     if (ret >= 0) {
> +             prim_bkt->signatures[ret].current = sig;
> +             prim_bkt->signatures[ret].alt = alt_hash;
> +             prim_bkt->key_idx[ret] = new_idx;
> +             return (new_idx - 1);
> +     }
> +
> +     /* Error in addition, store new slot back in the ring */
> +     odph_ring_sp_enqueue(
> +                     h->free_slots, (void *)((uintptr_t)new_idx));
> +     return ret;
> +}
> +
> +int32_t
> +odph_cuckoo_hash_add_key_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h, const void *key, uint32_t sig)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_add_key_with_hash(h, key, sig, 0);
> +}
> +
> +int32_t
> +odph_cuckoo_hash_add_key(const odph_cuckoo_hash_tbl_t *h, const void *key)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_add_key_with_hash(
> +                     h, key, hash(h, key), 0);
> +}
> +
> +int
> +odph_cuckoo_hash_add_key_with_hash_data(
> +             const odph_cuckoo_hash_tbl_t *h,
> +             const void *key, uint32_t sig, void *data)
> +{
> +     int ret;
> +
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     ret = cuckoo_hash_add_key_with_hash(h, key, sig, data);
> +     if (ret >= 0)
> +             return 0;
> +     return ret;
> +}
> +
> +int
> +odph_cuckoo_hash_add_key_data(const odph_cuckoo_hash_tbl_t *h,
> +                           const void *key, void *data)
> +{
> +     int ret;
> +
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +
> +     ret = cuckoo_hash_add_key_with_hash(
> +                     h, key, hash(h, key), data);
> +     if (ret >= 0)
> +             return 0;
> +     return ret;
> +}
> +
> +static inline int32_t
> +cuckoo_hash_lookup_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h, const void *key,
> +             uint32_t sig, void **data)
> +{
> +     uint32_t bucket_idx;
> +     uint32_t alt_hash;
> +     unsigned i;
> +     struct cuckoo_hash_bucket *bkt;
> +     struct cuckoo_hash_key *k, *keys = h->key_store;
> +
> +     bucket_idx = sig & h->bucket_bitmask;
> +     bkt = &h->buckets[bucket_idx];
> +
> +     /* Check if key is in primary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     bkt->signatures[i].current == sig &&
> +                     bkt->signatures[i].sig != NULL_SIGNATURE) {
> +                     k = (void *)((uintptr_t)keys +
> +                                     (bkt->key_idx[i] * h->key_entry_size));
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             if (data != NULL)
> +                                     *data = k->pdata;
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     /* Calculate secondary hash */
> +     alt_hash = hash_secondary(sig);
> +     bucket_idx = alt_hash & h->bucket_bitmask;
> +     bkt = &h->buckets[bucket_idx];
> +
> +     /* Check if key is in secondary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     bkt->signatures[i].current == alt_hash &&
> +                     bkt->signatures[i].alt == sig) {
> +                     k = (struct cuckoo_hash_key *)((char *)keys +
> +                                     bkt->key_idx[i] * h->key_entry_size);
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             if (data != NULL)
> +                                     *data = k->pdata;
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     return -ENOENT;
> +}
> +
> +int32_t
> +odph_cuckoo_hash_lookup_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h, const void *key, uint32_t sig)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_lookup_with_hash(h, key, sig, NULL);
> +}
> +
> +int32_t
> +odph_cuckoo_hash_lookup(const odph_cuckoo_hash_tbl_t *h, const void *key)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_lookup_with_hash(
> +                     h, key, hash(h, key), NULL);
> +}
> +
> +int
> +odph_cuckoo_hash_lookup_with_hash_data(
> +             const odph_cuckoo_hash_tbl_t *h,
> +             const void *key, uint32_t sig, void **data)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_lookup_with_hash(h, key, sig, data);
> +}
> +
> +int
> +odph_cuckoo_hash_lookup_data(
> +             const odph_cuckoo_hash_tbl_t *h,
> +             const void *key, void **data)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_lookup_with_hash(
> +                     h, key, hash(h, key), data);
> +}
> +
> +static inline int32_t
> +cuckoo_hash_del_key_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h,
> +             const void *key, uint32_t sig)
> +{
> +     uint32_t bucket_idx;
> +     uint32_t alt_hash;
> +     unsigned i;
> +     struct cuckoo_hash_bucket *bkt;
> +     struct cuckoo_hash_key *k, *keys = h->key_store;
> +
> +     bucket_idx = sig & h->bucket_bitmask;
> +     bkt = &h->buckets[bucket_idx];
> +
> +     /* Check if key is in primary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     bkt->signatures[i].current == sig &&
> +                     bkt->signatures[i].sig != NULL_SIGNATURE) {
> +                     k = (struct cuckoo_hash_key *)((char *)keys +
> +                                     bkt->key_idx[i] * h->key_entry_size);
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             bkt->signatures[i].sig = NULL_SIGNATURE;
> +                             odph_ring_sp_enqueue(
> +                                     h->free_slots,
> +                                     (void *)((uintptr_t)bkt->key_idx[i]));
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     /* Calculate secondary hash */
> +     alt_hash = hash_secondary(sig);
> +     bucket_idx = alt_hash & h->bucket_bitmask;
> +     bkt = &h->buckets[bucket_idx];
> +
> +     /* Check if key is in secondary location */
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             if (
> +                     bkt->signatures[i].current == alt_hash &&
> +                     bkt->signatures[i].sig != NULL_SIGNATURE) {
> +                     k = (struct cuckoo_hash_key *)((char *)keys +
> +                                     bkt->key_idx[i] * h->key_entry_size);
> +                     if (h->hash_cmp_eq(key, k->key, h->key_len) == 0) {
> +                             bkt->signatures[i].sig = NULL_SIGNATURE;
> +                             odph_ring_sp_enqueue(
> +                                     h->free_slots,
> +                                     (void *)((uintptr_t)bkt->key_idx[i]));
> +                             /*
> +                              * Return index where key is stored,
> +                              * substracting the first dummy index
> +                              */
> +                             return (bkt->key_idx[i] - 1);
> +                     }
> +             }
> +     }
> +
> +     return -ENOENT;
> +}
> +
> +int32_t
> +odph_cuckoo_hash_del_key_with_hash(
> +             const odph_cuckoo_hash_tbl_t *h,
> +             const void *key, uint32_t sig)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_del_key_with_hash(h, key, sig);
> +}
> +
> +int32_t
> +odph_cuckoo_hash_del_key(const odph_cuckoo_hash_tbl_t *h, const void *key)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (key == NULL)), -EINVAL);
> +     return cuckoo_hash_del_key_with_hash(
> +                     h, key, hash(h, key));
> +}
> +
> +/* Lookup bulk stage 0: Prefetch input key */
> +static inline void
> +lookup_stage0(
> +             unsigned *idx, uint64_t *lookup_mask,
> +             const void * const *keys)
> +{
> +     *idx = __builtin_ctzl(*lookup_mask);
> +     if (*lookup_mask == 0)
> +             *idx = 0;
> +
> +     __builtin_prefetch((const void *)(uintptr_t)(keys[*idx]), 0, 3);
> +     *lookup_mask &= ~(1llu << *idx);
> +}
> +
> +/*
> + * Lookup bulk stage 1: Calculate primary/secondary hashes
> + * and prefetch primary/secondary buckets
> + */
> +static inline void
> +lookup_stage1(
> +             unsigned idx, uint32_t *prim_hash, uint32_t *sec_hash,
> +             const struct cuckoo_hash_bucket **primary_bkt,
> +             const struct cuckoo_hash_bucket **secondary_bkt,
> +             uint32_t *hash_vals, const void * const *keys,
> +             const odph_cuckoo_hash_tbl_t *h)
> +{
> +     *prim_hash = hash(h, keys[idx]);
> +     hash_vals[idx] = *prim_hash;
> +     *sec_hash = hash_secondary(*prim_hash);
> +
> +     *primary_bkt = &h->buckets[*prim_hash & h->bucket_bitmask];
> +     *secondary_bkt = &h->buckets[*sec_hash & h->bucket_bitmask];
> +
> +     __builtin_prefetch((const void *)(uintptr_t)(*primary_bkt), 0, 3);
> +     __builtin_prefetch((const void *)(uintptr_t)(*secondary_bkt), 0, 3);
> +}
> +
> +/*
> + * Lookup bulk stage 2:  Search for match hashes in primary/secondary 
> locations
> + * and prefetch first key slot
> + */
> +static inline void
> +lookup_stage2(
> +             unsigned idx, uint32_t prim_hash, uint32_t sec_hash,
> +             const struct cuckoo_hash_bucket *prim_bkt,
> +             const struct cuckoo_hash_bucket *sec_bkt,
> +             const struct cuckoo_hash_key **key_slot, int32_t *positions,
> +             uint64_t *extra_hits_mask, const void *keys,
> +             const odph_cuckoo_hash_tbl_t *h)
> +{
> +     unsigned prim_hash_matches, sec_hash_matches, key_idx, i;
> +     unsigned total_hash_matches;
> +
> +     prim_hash_matches = 1 << HASH_BUCKET_ENTRIES;
> +     sec_hash_matches = 1 << HASH_BUCKET_ENTRIES;
> +     for (i = 0; i < HASH_BUCKET_ENTRIES; i++) {
> +             prim_hash_matches |=
> +                     ((prim_hash == prim_bkt->signatures[i].current) << i);
> +             sec_hash_matches |=
> +                     ((sec_hash == sec_bkt->signatures[i].current) << i);
> +     }
> +
> +     key_idx = prim_bkt->key_idx[__builtin_ctzl(prim_hash_matches)];
> +     if (key_idx == 0)
> +             key_idx = sec_bkt->key_idx[__builtin_ctzl(sec_hash_matches)];
> +
> +     total_hash_matches = (prim_hash_matches |
> +                             (sec_hash_matches <<
> +                             (HASH_BUCKET_ENTRIES + 1)));
> +     *key_slot = (const struct cuckoo_hash_key *)((const char *)keys +
> +                                     key_idx * h->key_entry_size);
> +
> +     __builtin_prefetch((const void *)(uintptr_t)(*key_slot), 0, 3);
> +     /*
> +      * Return index where key is stored,
> +      * substracting the first dummy index
> +      */
> +     positions[idx] = (key_idx - 1);
> +
> +     *extra_hits_mask |= (uint64_t)
> +             (__builtin_popcount(total_hash_matches) > 3) << idx;
> +}
> +
> +/* Lookup bulk stage 3: Check if key matches, update hit mask and return 
> data */
> +static inline void
> +lookup_stage3(
> +             unsigned idx, const struct cuckoo_hash_key *key_slot,
> +             const void * const *keys,
> +             void *data[], uint64_t *hits, const odph_cuckoo_hash_tbl_t *h)
> +{
> +     unsigned hit;
> +
> +     hit = !h->hash_cmp_eq(key_slot->key, keys[idx], h->key_len);
> +     if (data != NULL)
> +             data[idx] = key_slot->pdata;
> +
> +     *hits |= (uint64_t)(hit) << idx;
> +}
> +
> +static inline void
> +cuckoo_hash_lookup_bulk(
> +             const odph_cuckoo_hash_tbl_t *h, const void **keys,
> +             uint32_t num_keys, int32_t *positions,
> +             uint64_t *hit_mask, void *data[])
> +{
> +     uint64_t hits = 0;
> +     uint64_t extra_hits_mask = 0;
> +     uint64_t lookup_mask, miss_mask;
> +     unsigned idx;
> +     const void *key_store = h->key_store;
> +     int ret;
> +     uint32_t hash_vals[HASH_LOOKUP_BULK_MAX];
> +
> +     unsigned idx00, idx01, idx10, idx11, idx20, idx21, idx30, idx31;
> +     const struct cuckoo_hash_bucket *primary_bkt10, *primary_bkt11;
> +     const struct cuckoo_hash_bucket *secondary_bkt10, *secondary_bkt11;
> +     const struct cuckoo_hash_bucket *primary_bkt20, *primary_bkt21;
> +     const struct cuckoo_hash_bucket *secondary_bkt20, *secondary_bkt21;
> +     const struct cuckoo_hash_key *k_slot20, *k_slot21, *k_slot30, *k_slot31;
> +     uint32_t primary_hash10, primary_hash11;
> +     uint32_t secondary_hash10, secondary_hash11;
> +     uint32_t primary_hash20, primary_hash21;
> +     uint32_t secondary_hash20, secondary_hash21;
> +
> +     lookup_mask = (uint64_t)-1 >> (64 - num_keys);
> +     miss_mask = lookup_mask;
> +
> +     lookup_stage0(&idx00, &lookup_mask, keys);
> +     lookup_stage0(&idx01, &lookup_mask, keys);
> +
> +     idx10 = idx00, idx11 = idx01;
> +
> +     lookup_stage0(&idx00, &lookup_mask, keys);
> +     lookup_stage0(&idx01, &lookup_mask, keys);
> +     lookup_stage1(
> +                     idx10, &primary_hash10, &secondary_hash10,
> +                     &primary_bkt10, &secondary_bkt10, hash_vals, keys, h);
> +     lookup_stage1(
> +                     idx11, &primary_hash11, &secondary_hash11,
> +                     &primary_bkt11, &secondary_bkt11, hash_vals, keys, h);
> +
> +     primary_bkt20 = primary_bkt10;
> +     primary_bkt21 = primary_bkt11;
> +     secondary_bkt20 = secondary_bkt10;
> +     secondary_bkt21 = secondary_bkt11;
> +     primary_hash20 = primary_hash10;
> +     primary_hash21 = primary_hash11;
> +     secondary_hash20 = secondary_hash10;
> +     secondary_hash21 = secondary_hash11;
> +     idx20 = idx10, idx21 = idx11;
> +     idx10 = idx00, idx11 = idx01;
> +
> +     lookup_stage0(&idx00, &lookup_mask, keys);
> +     lookup_stage0(&idx01, &lookup_mask, keys);
> +     lookup_stage1(
> +                     idx10, &primary_hash10, &secondary_hash10,
> +                     &primary_bkt10, &secondary_bkt10, hash_vals, keys, h);
> +     lookup_stage1(
> +                     idx11, &primary_hash11, &secondary_hash11,
> +                     &primary_bkt11, &secondary_bkt11, hash_vals, keys, h);
> +     lookup_stage2(
> +                     idx20, primary_hash20, secondary_hash20, primary_bkt20,
> +                     secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
> +                     key_store, h);
> +     lookup_stage2(
> +                     idx21, primary_hash21, secondary_hash21, primary_bkt21,
> +                     secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
> +                     key_store, h);
> +
> +     while (lookup_mask) {
> +             k_slot30 = k_slot20, k_slot31 = k_slot21;
> +             idx30 = idx20, idx31 = idx21;
> +             primary_bkt20 = primary_bkt10;
> +             primary_bkt21 = primary_bkt11;
> +             secondary_bkt20 = secondary_bkt10;
> +             secondary_bkt21 = secondary_bkt11;
> +             primary_hash20 = primary_hash10;
> +             primary_hash21 = primary_hash11;
> +             secondary_hash20 = secondary_hash10;
> +             secondary_hash21 = secondary_hash11;
> +             idx20 = idx10, idx21 = idx11;
> +             idx10 = idx00, idx11 = idx01;
> +
> +             lookup_stage0(&idx00, &lookup_mask, keys);
> +             lookup_stage0(&idx01, &lookup_mask, keys);
> +             lookup_stage1(
> +                             idx10, &primary_hash10, &secondary_hash10,
> +                             &primary_bkt10, &secondary_bkt10, hash_vals,
> +                             keys, h);
> +             lookup_stage1(
> +                             idx11, &primary_hash11, &secondary_hash11,
> +                             &primary_bkt11, &secondary_bkt11, hash_vals,
> +                             keys, h);
> +             lookup_stage2(
> +                             idx20, primary_hash20, secondary_hash20,
> +                             primary_bkt20, secondary_bkt20, &k_slot20,
> +                             positions, &extra_hits_mask, key_store, h);
> +             lookup_stage2(
> +                             idx21, primary_hash21, secondary_hash21,
> +                             primary_bkt21, secondary_bkt21, &k_slot21,
> +                             positions, &extra_hits_mask, key_store, h);
> +             lookup_stage3(idx30, k_slot30, keys, data, &hits, h);
> +             lookup_stage3(idx31, k_slot31, keys, data, &hits, h);
> +     }
> +
> +     k_slot30 = k_slot20, k_slot31 = k_slot21;
> +     idx30 = idx20, idx31 = idx21;
> +     primary_bkt20 = primary_bkt10;
> +     primary_bkt21 = primary_bkt11;
> +     secondary_bkt20 = secondary_bkt10;
> +     secondary_bkt21 = secondary_bkt11;
> +     primary_hash20 = primary_hash10;
> +     primary_hash21 = primary_hash11;
> +     secondary_hash20 = secondary_hash10;
> +     secondary_hash21 = secondary_hash11;
> +     idx20 = idx10, idx21 = idx11;
> +     idx10 = idx00, idx11 = idx01;
> +
> +     lookup_stage1(
> +                     idx10, &primary_hash10, &secondary_hash10,
> +                     &primary_bkt10, &secondary_bkt10, hash_vals, keys, h);
> +     lookup_stage1(
> +                     idx11, &primary_hash11, &secondary_hash11,
> +                     &primary_bkt11, &secondary_bkt11, hash_vals, keys, h);
> +     lookup_stage2(
> +                     idx20, primary_hash20, secondary_hash20, primary_bkt20,
> +                     secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
> +                     key_store, h);
> +     lookup_stage2(
> +                     idx21, primary_hash21, secondary_hash21, primary_bkt21,
> +                     secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
> +                     key_store, h);
> +     lookup_stage3(idx30, k_slot30, keys, data, &hits, h);
> +     lookup_stage3(idx31, k_slot31, keys, data, &hits, h);
> +
> +     k_slot30 = k_slot20, k_slot31 = k_slot21;
> +     idx30 = idx20, idx31 = idx21;
> +     primary_bkt20 = primary_bkt10;
> +     primary_bkt21 = primary_bkt11;
> +     secondary_bkt20 = secondary_bkt10;
> +     secondary_bkt21 = secondary_bkt11;
> +     primary_hash20 = primary_hash10;
> +     primary_hash21 = primary_hash11;
> +     secondary_hash20 = secondary_hash10;
> +     secondary_hash21 = secondary_hash11;
> +     idx20 = idx10, idx21 = idx11;
> +
> +     lookup_stage2(
> +                     idx20, primary_hash20, secondary_hash20, primary_bkt20,
> +                     secondary_bkt20, &k_slot20, positions, &extra_hits_mask,
> +                     key_store, h);
> +     lookup_stage2(
> +                     idx21, primary_hash21, secondary_hash21, primary_bkt21,
> +                     secondary_bkt21, &k_slot21, positions, &extra_hits_mask,
> +                     key_store, h);
> +     lookup_stage3(idx30, k_slot30, keys, data, &hits, h);
> +     lookup_stage3(idx31, k_slot31, keys, data, &hits, h);
> +
> +     k_slot30 = k_slot20, k_slot31 = k_slot21;
> +     idx30 = idx20, idx31 = idx21;
> +
> +     lookup_stage3(idx30, k_slot30, keys, data, &hits, h);
> +     lookup_stage3(idx31, k_slot31, keys, data, &hits, h);
> +
> +     /* ignore any items we have already found */
> +     extra_hits_mask &= ~hits;
> +
> +     if (odp_unlikely(extra_hits_mask)) {
> +             /* run a single search for each remaining item */
> +             do {
> +                     idx = __builtin_ctzl(extra_hits_mask);
> +                     if (data != NULL) {
> +                             ret = odph_cuckoo_hash_lookup_with_hash_data(
> +                                             h, keys[idx], hash_vals[idx],
> +                                             &data[idx]);
> +                             if (ret >= 0)
> +                                     hits |= 1ULL << idx;
> +                     } else {
> +                             positions[idx] =
> +                                     odph_cuckoo_hash_lookup_with_hash(
> +                                             h, keys[idx], hash_vals[idx]);
> +                             if (positions[idx] >= 0)
> +                                     hits |= 1llu << idx;
> +                     }
> +                     extra_hits_mask &= ~(1llu << idx);
> +             } while (extra_hits_mask);
> +     }
> +
> +     miss_mask &= ~hits;
> +     if (odp_unlikely(miss_mask)) {
> +             do {
> +                     idx = __builtin_ctzl(miss_mask);
> +                     positions[idx] = -ENOENT;
> +                     miss_mask &= ~(1llu << idx);
> +             } while (miss_mask);
> +     }
> +
> +     if (hit_mask != NULL)
> +             *hit_mask = hits;
> +}
> +
> +int
> +odph_cuckoo_hash_lookup_bulk(
> +             const odph_cuckoo_hash_tbl_t *h, const void **keys,
> +             uint32_t num_keys, int32_t *positions)
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || (num_keys == 0) ||
> +                     (num_keys > HASH_LOOKUP_BULK_MAX) ||
> +                     (positions == NULL)), -EINVAL);
> +
> +     cuckoo_hash_lookup_bulk(
> +                     h, keys, num_keys, positions, NULL, NULL);
> +     return 0;
> +}
> +
> +int
> +odph_cuckoo_hash_lookup_bulk_data(
> +             const odph_cuckoo_hash_tbl_t *h, const void **keys,
> +             uint32_t num_keys, uint64_t *hit_mask, void *data[])
> +{
> +     RETURN_IF_TRUE(((h == NULL) || (keys == NULL) || (num_keys == 0) ||
> +                     (num_keys > HASH_LOOKUP_BULK_MAX) ||
> +                     (hit_mask == NULL)), -EINVAL);
> +
> +     int32_t positions[num_keys];
> +
> +     cuckoo_hash_lookup_bulk(
> +                     h, keys, num_keys, positions, hit_mask, data);
> +
> +     /* Return number of hits */
> +     return __builtin_popcountl(*hit_mask);
> +}
> +
> +int32_t
> +odph_cuckoo_hash_iterate(
> +             const odph_cuckoo_hash_tbl_t *h, const void **key,
> +             void **data, uint32_t *next)
> +{
> +     uint32_t bucket_idx, idx, position;
> +     struct cuckoo_hash_key *next_key;
> +
> +     RETURN_IF_TRUE(((h == NULL) || (next == NULL)), -EINVAL);
> +
> +     const uint32_t total_entries =
> +             h->num_buckets * HASH_BUCKET_ENTRIES;
> +     /* Out of bounds */
> +     if (*next >= total_entries)
> +             return -ENOENT;
> +
> +     /* Calculate bucket and index of current iterator */
> +     bucket_idx = *next / HASH_BUCKET_ENTRIES;
> +     idx = *next % HASH_BUCKET_ENTRIES;
> +
> +     /* If current position is empty, go to the next one */
> +     while (h->buckets[bucket_idx].signatures[idx].sig == NULL_SIGNATURE) {
> +             (*next)++;
> +             /* End of table */
> +             if (*next == total_entries)
> +                     return -ENOENT;
> +             bucket_idx = *next / HASH_BUCKET_ENTRIES;
> +             idx = *next % HASH_BUCKET_ENTRIES;
> +     }
> +
> +     /* Get position of entry in key table */
> +     position = h->buckets[bucket_idx].key_idx[idx];
> +     next_key = (struct cuckoo_hash_key *)((char *)h->key_store +
> +                             position * h->key_entry_size);
> +     /* Return key and data */
> +     *key = next_key->key;
> +     *data = next_key->pdata;
> +
> +     /* Increment iterator */
> +     (*next)++;
> +
> +     return (position - 1);
> +}
> diff --git a/helper/include/odp/helper/cuckoo_hash.h 
> b/helper/include/odp/helper/cuckoo_hash.h
> new file mode 100644
> index 0000000..f21b8a2
> --- /dev/null
> +++ b/helper/include/odp/helper/cuckoo_hash.h
> @@ -0,0 +1,435 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef ODPH_CUCKOO_HASH_H_
> +#define ODPH_CUCKOO_HASH_H_
> +
> +#include <odp/shared_memory.h>
> +#include <odp/helper/ring.h>
> +#include <odp/hash.h>
> +#include <stdint.h>
> +
> +/**
> + * @file
> + *
> + * ODP Cuckoo Hash Table Implementation
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/** Maximum number of characters in hash name.*/
> +#define ODPH_CUCKOO_HASH_NAMESIZE    32
> +
> +/** A hash table structure. */
> +typedef struct odph_cuckoo_hash_tbl {
> +     /**< Name of the hash. */
> +     char name[ODPH_CUCKOO_HASH_NAMESIZE];
> +     /**< Total table entries. */
> +     uint32_t entries;
> +     /**< Number of buckets in table. */
> +     uint32_t num_buckets;
> +     /**< Length of hash key. */
> +     uint32_t key_len;
> +     /**< Function used to calculate hash. */
> +     odp_hash_function hash_func;
> +     /**< Init value used by hash_func. */
> +     uint32_t hash_func_init_val;
> +     /**< Function used to compare keys. */
> +     odp_hash_cmp_eq_t hash_cmp_eq;
> +     /**< Bitmask for getting bucket index from hash signature. */
> +     uint32_t bucket_bitmask;
> +     /**< Size of each key entry. */
> +     uint32_t key_entry_size;
> +     /**< Ring that stores all indexes of the free slots in the key table */
> +     odph_ring_t *free_slots;
> +     /**< Table storing all keys and data */
> +     void *key_store;
> +     /** Table with buckets storing all the hash values and key indexes
> +       to the key table*/
> +     struct cuckoo_hash_bucket *buckets;
> +     /** memory used to store keys*/
> +     odp_shm_t shm_key;
> +     /** memory used to store buckets*/
> +     odp_shm_t shm_bucket;
> +} odph_cuckoo_hash_tbl_t ODP_ALIGNED_CACHE;
> +
> +/**
> + * Parameters used when creating the cuckoo hash table.
> + */
> +typedef struct odph_cuckoo_hash_param_t {
> +     /**< Name of the hash table. */
> +     const char *name;
> +     /**< Total hash table entries. */
> +     uint32_t entries;
> +     /**< Length of hash key. */
> +     uint32_t key_len;
> +     /**< Primary Hash function used to calculate hash. */
> +     odp_hash_function hash_func;
> +     /**< Init value used by hash_func. */
> +     uint32_t hash_func_init_val;
> +     /**< Indicate if additional parameters are present. */
> +     uint8_t extra_flag;
> +} odph_cuckoo_hash_param_t;
> +
> +/**
> + * Create a new cuckoo hash table.
> + *
> + * @param ht
> + *   hash table if successful
> + * @param params
> + *   Parameters used to create and initialise the hash table.
> + * @return
> + *   0 on success
> + *   negative value on errors including:
> + *    - -EINVAL - invalid parameter passed to function
> + *    - -EEXIST - a hash table with the same name already exists
> + *    - -ENOMEM - no appropriate memory area found in which to create memzone
> + */
> +int
> +odph_cuckoo_hash_create(odph_cuckoo_hash_tbl_t **ht,
> +                     const odph_cuckoo_hash_param_t *params);
> +
> +/**
> + * Find an existing hash table object and return a pointer to it.
> + *
> + * @param name
> + *   Name of the hash table as passed to odph_hash_create()
> + * @return
> + *   Pointer to hash table or NULL if object not found
> + */
> +odph_cuckoo_hash_tbl_t *
> +odph_cuckoo_hash_find_existing(const char *name);
> +
> +/**
> + * De-allocate all memory used by hash table.
> + * @param h
> + *   Hash table to free
> + */
> +void
> +odph_cuckoo_hash_destroy(odph_cuckoo_hash_tbl_t *h);
> +
> +/**
> + * Reset all hash structure, by zeroing all entries
> + * @param h
> + *   Hash table to reset
> + */
> +void
> +odph_cuckoo_hash_reset(odph_cuckoo_hash_tbl_t *h);
> +
> +/**
> + * Add a key-value pair to an existing hash table.
> + * This operation is not multi-thread safe
> + * and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to add the key to.
> + * @param key
> + *   Key to add to the hash table.
> + * @param data
> + *   Data to add to the hash table.
> + * @return
> + *   - 0 if added successfully
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOSPC if there is no space in the hash for this key.
> + */
> +int
> +odph_cuckoo_hash_add_key_data(const odph_cuckoo_hash_tbl_t *h,
> +                           const void *key, void *data);
> +
> +/**
> + * Add a key-value pair with a pre-computed hash value
> + * to an existing hash table.
> + * This operation is not multi-thread safe
> + * and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to add the key to.
> + * @param key
> + *   Key to add to the hash table.
> + * @param sig
> + *   Precomputed hash value for 'key'
> + * @param data
> + *   Data to add to the hash table.
> + * @return
> + *   - 0 if added successfully
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOSPC if there is no space in the hash for this key.
> + */
> +int32_t
> +odph_cuckoo_hash_add_key_with_hash_data(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key,
> +     uint32_t sig, void *data);
> +
> +/**
> + * Add a key to an existing hash table. This operation is not multi-thread
> + * safe and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to add the key to.
> + * @param key
> + *   Key to add to the hash table.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOSPC if there is no space in the hash for this key.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key.
> + */
> +int32_t
> +odph_cuckoo_hash_add_key(const odph_cuckoo_hash_tbl_t *h, const void *key);
> +
> +/**
> + * Add a key to an existing hash table.
> + * This operation is not multi-thread safe
> + * and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to add the key to.
> + * @param key
> + *   Key to add to the hash table.
> + * @param sig
> + *   Precomputed hash value for 'key'.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOSPC if there is no space in the hash for this key.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key.
> + */
> +int32_t
> +odph_cuckoo_hash_add_key_with_hash(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key, uint32_t sig);
> +
> +/**
> + * Remove a key from an existing hash table.
> + * This operation is not multi-thread safe
> + * and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to remove the key from.
> + * @param key
> + *   Key to remove from the hash table.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOENT if the key is not found.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key, and is the same
> + *     value that was returned when the key was added.
> + */
> +int32_t
> +odph_cuckoo_hash_del_key(const odph_cuckoo_hash_tbl_t *h, const void *key);
> +
> +/**
> + * Remove a key from an existing hash table.
> + * This operation is not multi-thread safe
> + * and should only be called from one thread.
> + *
> + * @param h
> + *   Hash table to remove the key from.
> + * @param key
> + *   Key to remove from the hash table.
> + * @param sig
> + *   Precomputed hash value for 'key'.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOENT if the key is not found.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key, and is the same
> + *     value that was returned when the key was added.
> + */
> +int32_t
> +odph_cuckoo_hash_del_key_with_hash(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key, uint32_t sig);
> +
> +/**
> + * Find a key-value pair in the hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param key
> + *   Key to find.
> + * @param data
> + *   Output with pointer to data returned from the hash table.
> + * @return
> + *   0 if successful lookup
> + *   - EINVAL if the parameters are invalid.
> + *   - ENOENT if the key is not found.
> + */
> +int
> +odph_cukoo_hash_lookup_data(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key, void **data);
> +
> +/**
> + * Find a key-value pair with a pre-computed hash value
> + * to an existing hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param key
> + *   Key to find.
> + * @param sig
> + *   Precomputed hash value for 'key'
> + * @param data
> + *   Output with pointer to data returned from the hash table.
> + * @return
> + *   0 if successful lookup
> + *   - EINVAL if the parameters are invalid.
> + *   - ENOENT if the key is not found.
> + */
> +int
> +odph_cuckoo_hash_lookup_with_hash_data(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key,
> +     uint32_t sig, void **data);
> +
> +/**
> + * Find a key in the hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param key
> + *   Key to find.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOENT if the key is not found.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key, and is the same
> + *     value that was returned when the key was added.
> + */
> +int32_t
> +odph_cuckoo_hash_lookup(const odph_cuckoo_hash_tbl_t *h, const void *key);
> +
> +/**
> + * Find a key in the hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param key
> + *   Key to find.
> + * @param sig
> + *   Hash value to remove from the hash table.
> + * @return
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOENT if the key is not found.
> + *   - A positive value that can be used by the caller as an offset into an
> + *     array of user data. This value is unique for this key, and is the same
> + *     value that was returned when the key was added.
> + */
> +int32_t
> +odph_cuckoo_hash_lookup_with_hash(
> +     const odph_cuckoo_hash_tbl_t *h, const void *key, uint32_t sig);
> +
> +/**
> + * Find multiple keys in the hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param keys
> + *   A pointer to a list of keys to look for.
> + * @param num_keys
> + *   How many keys are in the keys list (less than 
> ODPH_HASH_LOOKUP_BULK_MAX).
> + * @param hit_mask
> + *   Output containing a bitmask with all successful lookups.
> + * @param data
> + *   Output containing array of data returned from all the successful 
> lookups.
> + * @return
> + *   -EINVAL if there's an error, otherwise number of successful lookups.
> + */
> +int
> +odph_cuckoo_hash_lookup_bulk_data(
> +     const odph_cuckoo_hash_tbl_t *h, const void **keys,
> +     uint32_t num_keys, uint64_t *hit_mask, void *data[]);
> +
> +/**
> + * Find multiple keys in the hash table.
> + * This operation is multi-thread safe.
> + *
> + * @param h
> + *   Hash table to look in.
> + * @param keys
> + *   A pointer to a list of keys to look for.
> + * @param num_keys
> + *   How many keys are in the keys list (less than HASH_LOOKUP_BULK_MAX).
> + * @param positions
> + *   Output containing a list of values, corresponding to the list of keys 
> that
> + *   can be used by the caller as an offset into an array of user data. These
> + *   values are unique for each key, and are the same values that were 
> returned
> + *   when each key was added. If a key in the list was not found, then 
> -ENOENT
> + *   will be the value.
> + * @return
> + *   -EINVAL if there's an error, otherwise 0.
> + */
> +int
> +odph_cuckoo_hash_lookup_bulk(
> +     const odph_cuckoo_hash_tbl_t *h, const void **keys,
> +     uint32_t num_keys, int32_t *positions);
> +
> +/**
> + * Iterate through the hash table, returning key-value pairs.
> + *
> + * @param h
> + *   Hash table to iterate
> + * @param key
> + *   Output containing the key where current iterator
> + *   was pointing at
> + * @param data
> + *   Output containing the data associated with key.
> + *   Returns NULL if data was not stored.
> + * @param next
> + *   Pointer to iterator. Should be 0 to start iterating the hash table.
> + *   Iterator is incremented after each call of this function.
> + * @return
> + *   Position where key was stored, if successful.
> + *   - -EINVAL if the parameters are invalid.
> + *   - -ENOENT if end of the hash table.
> + */
> +int32_t
> +odph_cuckoo_hash_iterate(
> +     const odph_cuckoo_hash_tbl_t *h,
> +     const void **key, void **data, uint32_t *next);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* ODPH_HASH_H_ */
> diff --git a/helper/include/odp/helper/ring.h 
> b/helper/include/odp/helper/ring.h
> index 65c32ad..3c1a3bf 100644
> --- a/helper/include/odp/helper/ring.h
> +++ b/helper/include/odp/helper/ring.h
> @@ -132,6 +132,8 @@ typedef struct odph_ring {
>       /** @private Flags supplied at creation. */
>       int flags;
> 
> +     odp_shm_t shm;
> +
>       /** @private Producer */
>       struct prod {
>               uint32_t watermark;      /* Maximum items */
> @@ -194,6 +196,18 @@ typedef struct odph_ring {
> odph_ring_t *odph_ring_create(const char *name, unsigned count,
>                           unsigned flags);
> 
> +/**
> + * Free a ring in memory.
> + *
> + * This function uses odp_shm_reserve() to allocate memory. Its size is
> + * set to *count*, which must be a power of two. Water marking is
> + * disabled by default. Note that the real usable ring size is count-1
> + * instead of count.
> + *
> + * @param r
> + *   The pointer to the ring need to be free.
> + */
> +void odph_ring_free(odph_ring_t *r);
> 
> /**
>  * Change the high water mark.
> @@ -372,6 +386,21 @@ int odph_ring_sp_enqueue_bulk(odph_ring_t *r, void * 
> const *obj_table,
>                             unsigned n);
> 
> /**
> + * Enqueue one object on a ring (NOT multi-producers safe).
> + *
> + * @param r
> + *   A pointer to the ring structure.
> + * @param obj
> + *   A pointer to the object to be added.
> + * @return
> + *   - 0: Success; objects enqueued.
> + *   - -EDQUOT: Quota exceeded. The objects have been enqueued, but the
> + *     high water mark is exceeded.
> + *   - -ENOBUFS: Not enough room in the ring to enqueue; no object is 
> enqueued.
> + */
> +int odph_ring_sp_enqueue(odph_ring_t *r, void *obj);
> +
> +/**
>  * Dequeue several objects from a ring (multi-consumers safe).
>  *
>  * This function uses a "compare and set" instruction to move the
> @@ -408,6 +437,20 @@ int odph_ring_mc_dequeue_bulk(odph_ring_t *r, void 
> **obj_table, unsigned n);
> int odph_ring_sc_dequeue_bulk(odph_ring_t *r, void **obj_table, unsigned n);
> 
> /**
> + * Dequeue one object from a ring (NOT multi-consumers safe).
> + *
> + * @param r
> + *   A pointer to the ring structure.
> + * @param obj_p
> + *   A pointer to a void * pointer (object) that will be filled.
> + * @return
> + *   - 0: Success; objects dequeued.
> + *   - -ENOENT: Not enough entries in the ring to dequeue, no object is
> + *     dequeued.
> + */
> +int odph_ring_sc_dequeue(odph_ring_t *r, void *obj);
> +
> +/**
>  * Test if a ring is full.
>  *
>  * @param r
> diff --git a/helper/ring.c b/helper/ring.c
> index 3122173..afd7244 100644
> --- a/helper/ring.c
> +++ b/helper/ring.c
> @@ -174,12 +174,18 @@ odph_ring_create(const char *name, unsigned count, 
> unsigned flags)
>       /* reserve a memory zone for this ring.*/
>       shm = odp_shm_reserve(ring_name, ring_size, ODP_CACHE_LINE_SIZE, 0);
> 
> +     if (shm == ODP_SHM_INVALID) {
> +             ODPH_ERR("shm allocation failed for ring %s\n", ring_name);
> +             return NULL;
> +     }
> +
>       r = odp_shm_addr(shm);
> 
>       if (r != NULL) {
>               /* init the ring structure */
>               snprintf(r->name, sizeof(r->name), "%s", name);
>               r->flags = flags;
> +             r->shm = shm;
>               r->prod.watermark = count;
>               r->prod.sp_enqueue = !!(flags & ODPH_RING_F_SP_ENQ);
>               r->cons.sc_dequeue = !!(flags & ODPH_RING_F_SC_DEQ);
> @@ -201,6 +207,18 @@ odph_ring_create(const char *name, unsigned count, 
> unsigned flags)
>       return r;
> }
> 
> +/* free a ring */
> +void
> +odph_ring_free(odph_ring_t *r)
> +{
> +     odp_rwlock_write_lock(&qlock);
> +     TAILQ_REMOVE(&odp_ring_list, r, next);
> +     odp_rwlock_write_unlock(&qlock);
> +
> +     if (r->shm != ODP_SHM_INVALID)
> +             odp_shm_free(r->shm);
> +}
> +
> /*
>  * change the high water mark. If *count* is 0, water marking is
>  * disabled
> @@ -471,6 +489,11 @@ int odph_ring_sp_enqueue_bulk(odph_ring_t *r, void * 
> const *obj_table,
>                                        ODPH_RING_QUEUE_FIXED);
> }
> 
> +int odph_ring_sp_enqueue(odph_ring_t *r, void *obj)
> +{
> +     return odph_ring_sp_enqueue_bulk(r, &obj, 1);
> +}
> +
> /**
>  * Dequeue several objects from a ring (multi-consumers safe).
>  */
> @@ -489,6 +512,11 @@ int odph_ring_sc_dequeue_bulk(odph_ring_t *r, void 
> **obj_table, unsigned n)
>                                        ODPH_RING_QUEUE_FIXED);
> }
> 
> +int odph_ring_sc_dequeue(odph_ring_t *r, void *obj)
> +{
> +     return odph_ring_sc_dequeue_bulk(r, &obj, 1);
> +}
> +
> /**
>  * Test if a ring is full.
>  */
> diff --git a/helper/test/Makefile.am b/helper/test/Makefile.am
> index d6820e1..e358a87 100644
> --- a/helper/test/Makefile.am
> +++ b/helper/test/Makefile.am
> @@ -9,7 +9,8 @@ EXECUTABLES = odp_chksum$(EXEEXT) \
>               odp_thread$(EXEEXT) \
>               odp_process$(EXEEXT)\
>               odph_pause$(EXEEXT)\
> -              odp_table$(EXEEXT)
> +              odp_table$(EXEEXT)\
> +                       odp_cuckoo_hash$(EXEEXT)
> 
> COMPILE_ONLY =
> 
> @@ -31,3 +32,4 @@ dist_odp_process_SOURCES = odp_process.c
> odp_process_LDADD = $(LIB)/libodphelper.la $(LIB)/libodp.la
> odph_pause_SOURCES = odph_pause.c
> dist_odp_table_SOURCES = odp_table.c
> +dist_odp_cuckoo_hash_SOURCES = odp_cuckoo_hash.c
> diff --git a/helper/test/odp_cuckoo_hash.c b/helper/test/odp_cuckoo_hash.c
> new file mode 100644
> index 0000000..c51ba0a
> --- /dev/null
> +++ b/helper/test/odp_cuckoo_hash.c
> @@ -0,0 +1,779 @@
> +/* Copyright (c) 2015, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +/*-
> + *   BSD LICENSE
> + *
> + *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
> + *   All rights reserved.
> + *
> + *   Redistribution and use in source and binary forms, with or without
> + *   modification, are permitted provided that the following conditions
> + *   are met:
> + *
> + *     * Redistributions of source code must retain the above copyright
> + *       notice, this list of conditions and the following disclaimer.
> + *     * Redistributions in binary form must reproduce the above copyright
> + *       notice, this list of conditions and the following disclaimer in
> + *       the documentation and/or other materials provided with the
> + *       distribution.
> + *     * Neither the name of Intel Corporation nor the names of its
> + *       contributors may be used to endorse or promote products derived
> + *       from this software without specific prior written permission.
> + *
> + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdarg.h>
> +#include <errno.h>
> +#include <sys/queue.h>
> +#include <sys/time.h>
> +#include <time.h>
> +
> +#include <test_debug.h>
> +#include <odp.h>
> +#include <odp/helper/ring.h>
> +#include <odp/helper/cuckoo_hash.h>
> +
> +/*******************************************************************************
> + * Hash function performance test configuration section. Each performance 
> test
> + * will be performed HASHTEST_ITERATIONS times.
> + *
> + * The five arrays below control what tests are performed. Every combination
> + * from the array entries is tested.
> + */
> +odp_hash_function hashtest_funcs[] = {odp_hash_crc32c};
> +/******************************************************************************/
> +
> +/*
> + * Check condition and return an error if true. Assumes that "handle" is the
> + * name of the hash structure pointer to be freed.
> + */
> +#define RETURN_IF_ERROR(cond, str, ...) do {                         \
> +     if (cond) {                                                     \
> +             printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \
> +             if (handle) \
> +                     odph_cuckoo_hash_destroy(handle);               \
> +             return -1;                                              \
> +     }                                                               \
> +} while (0)
> +
> +/* 5-tuple key type */
> +struct flow_key {
> +     uint32_t ip_src;
> +     uint32_t ip_dst;
> +     uint16_t port_src;
> +     uint16_t port_dst;
> +     uint8_t proto;
> +} __packed;
> +
> +/*
> + * Print out result of unit test hash operation.
> + */
> +static void print_key_info(
> +             const char *msg, const struct flow_key *key,
> +             int32_t pos)
> +{
> +     const uint8_t *p = (const uint8_t *)key;
> +     unsigned i;
> +
> +     printf("%s key:0x", msg);
> +     for (i = 0; i < sizeof(struct flow_key); i++)
> +             printf("%02X", p[i]);
> +     printf(" @ pos %d\n", pos);
> +}
> +
> +static double get_time_diff(struct timeval *start, struct timeval *end)
> +{
> +     int sec = end->tv_sec - start->tv_sec;
> +     int usec = end->tv_usec - start->tv_usec;
> +
> +     if (usec < 0) {
> +             sec--;
> +             usec += 1000000;
> +     }
> +     double diff = sec + (double)usec / 1000000;
> +
> +     return diff;
> +}
> +
> +/** Create IPv4 address */
> +#define IPv4(a, b, c, d) ((uint32_t)(((a) & 0xff) << 24) | \
> +     (((b) & 0xff) << 16) | \
> +     (((c) & 0xff) << 8)  | \
> +     ((d) & 0xff))
> +
> +/* Keys used by unit test functions */
> +static struct flow_key keys[5] = { {
> +     .ip_src = IPv4(0x03, 0x02, 0x01, 0x00),
> +     .ip_dst = IPv4(0x07, 0x06, 0x05, 0x04),
> +     .port_src = 0x0908,
> +     .port_dst = 0x0b0a,
> +     .proto = 0x0c,
> +}, {
> +     .ip_src = IPv4(0x13, 0x12, 0x11, 0x10),
> +     .ip_dst = IPv4(0x17, 0x16, 0x15, 0x14),
> +     .port_src = 0x1918,
> +     .port_dst = 0x1b1a,
> +     .proto = 0x1c,
> +}, {
> +     .ip_src = IPv4(0x23, 0x22, 0x21, 0x20),
> +     .ip_dst = IPv4(0x27, 0x26, 0x25, 0x24),
> +     .port_src = 0x2928,
> +     .port_dst = 0x2b2a,
> +     .proto = 0x2c,
> +}, {
> +     .ip_src = IPv4(0x33, 0x32, 0x31, 0x30),
> +     .ip_dst = IPv4(0x37, 0x36, 0x35, 0x34),
> +     .port_src = 0x3938,
> +     .port_dst = 0x3b3a,
> +     .proto = 0x3c,
> +}, {
> +     .ip_src = IPv4(0x43, 0x42, 0x41, 0x40),
> +     .ip_dst = IPv4(0x47, 0x46, 0x45, 0x44),
> +     .port_src = 0x4948,
> +     .port_dst = 0x4b4a,
> +     .proto = 0x4c,
> +} };
> +
> +/* Parameters used for hash table in unit test functions. Name set later. */
> +static odph_cuckoo_hash_param_t ut_params = {
> +     .entries = 64,
> +     .key_len = sizeof(struct flow_key), /* 13 */
> +     .hash_func = odp_hash_crc32c,
> +     .hash_func_init_val = 0,
> +};
> +
> +#define CRC32_ITERATIONS 1048576
> +#define CRC32_DWORDS 64
> +
> +static uint32_t hash(const odph_cuckoo_hash_tbl_t *h, const void *key)
> +{
> +     /* calc hash result by key */
> +     return h->hash_func(key, h->key_len, h->hash_func_init_val);
> +}
> +
> +/*
> + * Basic sequence of operations for a single key:
> + *   - add
> + *   - lookup (hit)
> + *   - delete
> + *   - lookup (miss)
> + */
> +static int test_add_delete(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     /* test with standard add/lookup/delete functions */
> +     int pos0, expected_pos0;
> +     int ret;
> +
> +     ut_params.name = "test1";
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(ret < 0, "cuckoo hash creation failed");
> +
> +     pos0 = odph_cuckoo_hash_add_key(handle, &keys[0]);
> +     print_key_info("Add", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 < 0, "failed to add key (pos0=%d)", pos0);
> +     expected_pos0 = pos0;
> +
> +     pos0 = odph_cuckoo_hash_lookup(handle, &keys[0]);
> +     print_key_info("Lkp", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to find key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_del_key(handle, &keys[0]);
> +     print_key_info("Del", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to delete key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_lookup(handle, &keys[0]);
> +     print_key_info("Lkp", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != -ENOENT,
> +                     "fail: found key after deleting! (pos0=%d)", pos0);
> +
> +     odph_cuckoo_hash_destroy(handle);
> +
> +     /* repeat test with precomputed hash functions */
> +     uint32_t hash_value;
> +     int pos1, expected_pos1;
> +
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(handle == NULL, "hash creation failed");
> +
> +     hash_value = hash(handle, &keys[0]);
> +     pos1 = odph_cuckoo_hash_add_key_with_hash(handle, &keys[0], hash_value);
> +     print_key_info("Add", &keys[0], pos1);
> +     RETURN_IF_ERROR(pos1 < 0, "failed to add key (pos1=%d)", pos1);
> +     expected_pos1 = pos1;
> +
> +     pos1 = odph_cuckoo_hash_lookup_with_hash(handle, &keys[0], hash_value);
> +     print_key_info("Lkp", &keys[0], pos1);
> +     RETURN_IF_ERROR(pos1 != expected_pos1,
> +                     "failed to find key (pos1=%d)", pos1);
> +
> +     pos1 = odph_cuckoo_hash_del_key_with_hash(handle, &keys[0], hash_value);
> +     print_key_info("Del", &keys[0], pos1);
> +     RETURN_IF_ERROR(pos1 != expected_pos1,
> +                     "failed to delete key (pos1=%d)", pos1);
> +
> +     pos1 = odph_cuckoo_hash_lookup_with_hash(handle, &keys[0], hash_value);
> +     print_key_info("Lkp", &keys[0], pos1);
> +     RETURN_IF_ERROR(pos1 != -ENOENT,
> +                     "fail: found key after deleting! (pos1=%d)", pos1);
> +
> +     odph_cuckoo_hash_destroy(handle);
> +
> +     return 0;
> +}
> +
> +/*
> + * Sequence of operations for a single key:
> + *   - delete: miss
> + *   - add
> + *   - lookup: hit
> + *   - add: update
> + *   - lookup: hit (updated data)
> + *   - delete: hit
> + *   - delete: miss
> + *   - lookup: miss
> + */
> +static int test_add_update_delete(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     int pos0, expected_pos0;
> +     int ret;
> +
> +     ut_params.name = "test2";
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(ret < 0, "hash creation failed");
> +
> +     pos0 = odph_cuckoo_hash_del_key(handle, &keys[0]);
> +     print_key_info("Del", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != -ENOENT,
> +                     "fail: found non-existent key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_add_key(handle, &keys[0]);
> +     print_key_info("Add", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 < 0, "failed to add key (pos0=%d)", pos0);
> +     expected_pos0 = pos0;
> +
> +     pos0 = odph_cuckoo_hash_lookup(handle, &keys[0]);
> +     print_key_info("Lkp", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to find key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_add_key(handle, &keys[0]);
> +     print_key_info("Add", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to re-add key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_lookup(handle, &keys[0]);
> +     print_key_info("Lkp", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to find key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_del_key(handle, &keys[0]);
> +     print_key_info("Del", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != expected_pos0,
> +                     "failed to delete key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_del_key(handle, &keys[0]);
> +     print_key_info("Del", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != -ENOENT,
> +                     "fail: deleted already deleted key (pos0=%d)", pos0);
> +
> +     pos0 = odph_cuckoo_hash_lookup(handle, &keys[0]);
> +     print_key_info("Lkp", &keys[0], pos0);
> +     RETURN_IF_ERROR(pos0 != -ENOENT,
> +                     "fail: found key after deleting! (pos0=%d)", pos0);
> +
> +     odph_cuckoo_hash_destroy(handle);
> +     return 0;
> +}
> +
> +/*
> + * Sequence of operations for find existing hash table
> + *
> + *  - create table
> + *  - find existing table: hit
> + *  - find non-existing table: miss
> + *
> + */
> +static int test_hash_find_existing(void)
> +{
> +     int ret;
> +     odph_cuckoo_hash_tbl_t *handle = NULL, *result = NULL;
> +
> +     /* Create hash table. */
> +     ut_params.name = "hash_find_existing";
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(ret < 0, "hash creation failed");
> +
> +     /* Try to find existing hash table */
> +     result = odph_cuckoo_hash_find_existing("hash_find_existing");
> +     RETURN_IF_ERROR(result != handle, "could not find existing hash table");
> +
> +     /* Try to find non-existing hash table */
> +     result = odph_cuckoo_hash_find_existing("hash_find_non_existing");
> +     RETURN_IF_ERROR(!(result == NULL), "found table that shouldn't exist");
> +
> +     /* Cleanup. */
> +     odph_cuckoo_hash_destroy(handle);
> +
> +     return 0;
> +}
> +
> +/*
> + * Sequence of operations for 5 keys
> + *   - add keys
> + *   - lookup keys: hit
> + *   - add keys (update)
> + *   - lookup keys: hit (updated data)
> + *   - delete keys : hit
> + *   - lookup keys: miss
> + */
> +static int test_five_keys(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     const void *key_array[5] = {0};
> +     int pos[5];
> +     int expected_pos[5];
> +     unsigned i;
> +     int ret;
> +
> +     ut_params.name = "test3";
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(ret < 0, "hash creation failed");
> +
> +     /* Add */
> +     for (i = 0; i < 5; i++) {
> +             pos[i] = odph_cuckoo_hash_add_key(handle, &keys[i]);
> +             print_key_info("Add", &keys[i], pos[i]);
> +             RETURN_IF_ERROR(pos[i] < 0,
> +                             "failed to add key (pos[%u]=%d)", i, pos[i]);
> +             expected_pos[i] = pos[i];
> +     }
> +
> +     /* Lookup */
> +     for (i = 0; i < 5; i++)
> +             key_array[i] = &keys[i];
> +
> +     ret = odph_cuckoo_hash_lookup_bulk(handle, &key_array[0], 5,
> +                                        (int32_t *)pos);
> +     if (ret == 0)
> +             for (i = 0; i < 5; i++) {
> +                     print_key_info("Lkp", key_array[i], pos[i]);
> +                     RETURN_IF_ERROR(
> +                                     pos[i] != expected_pos[i],
> +                                     "failed to find key (pos[%u]=%d)",
> +                                     i, pos[i]);
> +             }
> +
> +     /* Add - update */
> +     for (i = 0; i < 5; i++) {
> +             pos[i] = odph_cuckoo_hash_add_key(handle, &keys[i]);
> +             print_key_info("Add", &keys[i], pos[i]);
> +             RETURN_IF_ERROR(
> +                             pos[i] != expected_pos[i],
> +                             "failed to add key (pos[%u]=%d)", i, pos[i]);
> +     }
> +
> +     /* Lookup */
> +     for (i = 0; i < 5; i++) {
> +             pos[i] = odph_cuckoo_hash_lookup(handle, &keys[i]);
> +             print_key_info("Lkp", &keys[i], pos[i]);
> +             RETURN_IF_ERROR(
> +                             pos[i] != expected_pos[i],
> +                             "failed to find key (pos[%u]=%d)", i, pos[i]);
> +     }
> +
> +     /* Delete */
> +     for (i = 0; i < 5; i++) {
> +             pos[i] = odph_cuckoo_hash_del_key(handle, &keys[i]);
> +             print_key_info("Del", &keys[i], pos[i]);
> +             RETURN_IF_ERROR(
> +                             pos[i] != expected_pos[i],
> +                             "failed to delete key (pos[%u]=%d)", i, pos[i]);
> +     }
> +
> +     /* Lookup */
> +     for (i = 0; i < 5; i++) {
> +             pos[i] = odph_cuckoo_hash_lookup(handle, &keys[i]);
> +             print_key_info("Lkp", &keys[i], pos[i]);
> +             RETURN_IF_ERROR(
> +                             pos[i] != -ENOENT,
> +                             "found non-existent key (pos[%u]=%d)",
> +                             i, pos[i]);
> +     }
> +
> +     /* Lookup multi */
> +     ret = odph_cuckoo_hash_lookup_bulk(handle, &key_array[0], 5,
> +                                        (int32_t *)pos);
> +     if (ret == 0)
> +             for (i = 0; i < 5; i++) {
> +                     print_key_info("Lkp", key_array[i], pos[i]);
> +                     RETURN_IF_ERROR(
> +                                     pos[i] != -ENOENT,
> +                                     "found not-existent key (pos[%u]=%d)",
> +                                     i, pos[i]);
> +             }
> +
> +     odph_cuckoo_hash_destroy(handle);
> +     return 0;
> +}
> +
> +#define BUCKET_ENTRIES 4
> +#define HASH_ENTRIES_MAX 1048576
> +/*
> + * Do tests for hash creation with bad parameters.
> + */
> +static int test_hash_creation_with_bad_parameters(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     odph_cuckoo_hash_param_t params;
> +     int ret;
> +
> +     ret = odph_cuckoo_hash_create(&handle, NULL);
> +     if (handle != NULL) {
> +             odph_cuckoo_hash_destroy(handle);
> +             printf("Impossible creating hash successfully without any 
> parameter\n");
> +             return -1;
> +     }
> +
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "creation_with_bad_parameters_0";
> +     params.entries = HASH_ENTRIES_MAX + 1;
> +     ret =  odph_cuckoo_hash_create(&handle, &params);
> +     if (handle != NULL) {
> +             odph_cuckoo_hash_destroy(handle);
> +             printf("Impossible creating hash successfully with entries in 
> parameter exceeded\n");
> +             return -1;
> +     }
> +
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "creation_with_bad_parameters_2";
> +     params.entries = BUCKET_ENTRIES - 1;
> +     ret = odph_cuckoo_hash_create(&handle, &params);
> +     if (handle != NULL || ret == 0) {
> +             odph_cuckoo_hash_destroy(handle);
> +             printf("Impossible creating hash successfully if entries less 
> than bucket_entries in parameter\n");
> +             return -1;
> +     }
> +
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "creation_with_bad_parameters_3";
> +     params.key_len = 0;
> +     ret = odph_cuckoo_hash_create(&handle, &params);
> +     if (handle != NULL) {
> +             odph_cuckoo_hash_destroy(handle);
> +             printf("Impossible creating hash successfully if key_len in 
> parameter is zero\n");
> +             return -1;
> +     }
> +
> +     odph_cuckoo_hash_destroy(handle);
> +     printf("# Test successful. No more errors expected\n");
> +
> +     return 0;
> +}
> +
> +/*
> + * Do tests for hash creation with parameters that look incorrect
> + * but are actually valid.
> + */
> +static int
> +test_hash_creation_with_good_parameters(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle, *tmp;
> +     odph_cuckoo_hash_param_t params;
> +     int ret;
> +
> +     /* create with null hash function - should choose DEFAULT_HASH_FUNC */
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "same_name";
> +     params.hash_func = NULL;
> +     ret = odph_cuckoo_hash_create(&handle, &params);
> +     if (handle == NULL || ret < 0) {
> +             printf("Creating hash with null hash_func failed\n");
> +             return -1;
> +     }
> +
> +     /* this test is trying to create a hash with the same
> +      * name as previous one. This should return a pointer
> +      * to the hash we previously created. The previous hash
> +      * isn't freed exactly for the purpose of it being in
> +      * the hash list.
> +      */
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "same_name";
> +     ret = odph_cuckoo_hash_create(&tmp, &params);
> +
> +     /* check if the returned handle is actually
> +        equal to the previous hash */
> +     if (handle != tmp) {
> +             odph_cuckoo_hash_destroy(handle);
> +             odph_cuckoo_hash_destroy(tmp);
> +             printf("Creating hash with existing name was successful\n");
> +             return -1;
> +     }
> +
> +     /* try creating hash when there already are hashes in
> +      * the list. The previous hash is not freed to have a
> +      * non-empty hash list. The other hash that's in the
> +      * list is still pointed to by "handle" var.
> +      */
> +     memcpy(&params, &ut_params, sizeof(params));
> +     params.name = "different_name";
> +     ret = odph_cuckoo_hash_create(&tmp, &params);
> +     if (tmp == NULL) {
> +             odph_cuckoo_hash_destroy(handle);
> +             printf("Creating hash with valid parameters failed\n");
> +             return -1;
> +     }
> +
> +     odph_cuckoo_hash_destroy(tmp);
> +     odph_cuckoo_hash_destroy(handle);
> +
> +     return 0;
> +}
> +
> +#define ITERATIONS 50
> +#define NUM_ENTRIES 1024
> +#define HASH_KEY_LENGTH_MAX 64
> +static int test_hash_iteration(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     unsigned i;
> +     uint8_t keys[NUM_ENTRIES][HASH_KEY_LENGTH_MAX];
> +     const void *next_key;
> +     void *next_data;
> +     void *data[NUM_ENTRIES];
> +     unsigned added_keys;
> +     uint32_t iter = 0;
> +     int ret = 0;
> +
> +     ut_params.entries = NUM_ENTRIES;
> +     ut_params.name = "test_hash_iteration";
> +     ut_params.hash_func = odp_hash_crc32c;
> +     ut_params.key_len = 16;
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(handle == NULL, "hash creation failed");
> +
> +     /* Add random entries until key cannot be added */
> +     for (added_keys = 0; added_keys < NUM_ENTRIES; added_keys++) {
> +             data[added_keys] = (void *)((uintptr_t)rand());
> +             for (i = 0; i < ut_params.key_len; i++)
> +                     keys[added_keys][i] = rand() % 255;
> +             ret = odph_cuckoo_hash_add_key_data(
> +                             handle, keys[added_keys], data[added_keys]);
> +             if (ret < 0)
> +                     break;
> +     }
> +
> +     /* Iterate through the hash table */
> +     while (odph_cuckoo_hash_iterate(handle,
> +                                     &next_key, &next_data, &iter) >= 0) {
> +             /* Search for the key in the list of keys added */
> +             for (i = 0; i < NUM_ENTRIES; i++) {
> +                     if (memcmp(next_key, keys[i], ut_params.key_len) == 0) {
> +                             if (next_data != data[i]) {
> +                                     printf("Data found in the hash table is 
> not");
> +                                     printf("The data added with the key\n");
> +                                     goto err;
> +                             }
> +                             added_keys--;
> +                             break;
> +                     }
> +             }
> +             if (i == NUM_ENTRIES) {
> +                     printf("Key found in the hash table was not added\n");
> +                     goto err;
> +             }
> +     }
> +
> +     /* Check if all keys have been iterated */
> +     if (added_keys != 0) {
> +             printf("There were still %u keys to iterate\n", added_keys);
> +             goto err;
> +     }
> +
> +     odph_cuckoo_hash_destroy(handle);
> +     return 0;
> +
> +err:
> +     odph_cuckoo_hash_destroy(handle);
> +     return -1;
> +}
> +
> +#define ITERATIONS 50
> +#define LOOKUP_TIMES 1000
> +/*
> + * Test to see the average table utilization (entries added/max entries)
> + * before hitting a random entry that cannot be added
> + */
> +static int test_average_table_utilization(void)
> +{
> +     odph_cuckoo_hash_tbl_t *handle;
> +     uint8_t simple_key[HASH_KEY_LENGTH_MAX],
> +                     lookup_key[LOOKUP_TIMES][HASH_KEY_LENGTH_MAX];
> +     unsigned i, j;
> +     unsigned added_keys, average_keys_added = 0;
> +     int ret, pos_arr[LOOKUP_TIMES];
> +     struct timeval start, end;
> +     const void *bulk_key[LOOKUP_TIMES] = {0};
> +     double add_time = 0, lookup_time = 0, bulk_time = 0;
> +
> +     printf("\n# Running test to determine average utilization\n");
> +     printf("  before adding elements begins to fail\n");
> +     printf("Measuring performance, please wait");
> +     fflush(stdout);
> +     ut_params.entries = 1 << 20;
> +     ut_params.name = "test_average_utilization";
> +     ut_params.hash_func = odp_hash_crc32c;
> +     ret = odph_cuckoo_hash_create(&handle, &ut_params);
> +     RETURN_IF_ERROR(handle == NULL, "hash creation failed");
> +
> +     for (j = 0; j < ITERATIONS; j++) {
> +             ret = 0;
> +             unsigned added_one = 0;
> +
> +             gettimeofday(&start, 0);
> +             /* Add random entries until key cannot be added */
> +             for (added_keys = 0; ret >= 0; added_keys++) {
> +                     for (i = 0; i < ut_params.key_len; i++) {
> +                             simple_key[i] = rand() % 255;
> +                             if (added_one < LOOKUP_TIMES) {
> +                                     lookup_key[added_one][i]
> +                                             = simple_key[i];
> +                             }
> +                     }
> +                     if (added_one < LOOKUP_TIMES)
> +                             bulk_key[added_one] = &lookup_key[added_one];
> +                     ret = odph_cuckoo_hash_add_key(handle, simple_key);
> +                     added_one++;
> +             }
> +             gettimeofday(&end, 0);
> +             add_time += get_time_diff(&start, &end);
> +             if (ret != -ENOSPC) {
> +                     printf("Unexpected error when adding keys\n");
> +                     odph_cuckoo_hash_destroy(handle);
> +                     return -1;
> +             }
> +
> +             gettimeofday(&start, 0);
> +             for (i = 0; i < LOOKUP_TIMES; i++)
> +                     pos_arr[i] = odph_cuckoo_hash_lookup(handle,
> +                                                          lookup_key[i]);
> +             gettimeofday(&end, 0);
> +             lookup_time += get_time_diff(&start, &end);
> +
> +             gettimeofday(&start, 0);
> +             for (i = 0; i < LOOKUP_TIMES / 64; i++) {
> +                     odph_cuckoo_hash_lookup_bulk(
> +                                     handle, &bulk_key[i * 64],
> +                                     64, &pos_arr[i * 64]);
> +             }
> +             gettimeofday(&end, 0);
> +
> +             bulk_time +=  get_time_diff(&start, &end);
> +
> +             average_keys_added += added_keys;
> +
> +             odph_cuckoo_hash_reset(handle);
> +
> +             /* Print a dot to show progress on operations */
> +             printf(".");
> +             fflush(stdout);
> +     }
> +
> +     average_keys_added /= ITERATIONS;
> +
> +     printf(
> +             "\nAverage table utilization = %.2f%% (%u/%u)\n",
> +             ((double)average_keys_added / ut_params.entries * 100),
> +             average_keys_added, ut_params.entries);
> +     printf(
> +             "Average insert time = %lf,", (add_time / ITERATIONS));
> +     printf(
> +             " lookup time = %lf, lookup_bulk time = %lf\n",
> +             (lookup_time / ITERATIONS), (bulk_time / ITERATIONS));
> +     odph_cuckoo_hash_destroy(handle);
> +
> +     return 0;
> +}
> +
> +/*
> + * Do all unit and performance tests.
> + */
> +static int
> +test_cuckoo_hash_table(void)
> +{
> +     odph_ring_tailq_init();
> +     if (test_add_delete() < 0)
> +             return -1;
> +     if (test_hash_find_existing() < 0)
> +             return -1;
> +     if (test_add_update_delete() < 0)
> +             return -1;
> +     if (test_five_keys() < 0)
> +             return -1;
> +     if (test_hash_creation_with_bad_parameters() < 0)
> +             return -1;
> +     if (test_hash_creation_with_good_parameters() < 0)
> +             return -1;
> +     if (test_hash_iteration() < 0)
> +             return -1;
> +     if (test_average_table_utilization() < 0)
> +             return -1;
> +
> +     return 0;
> +}
> +
> +int main(int argc TEST_UNUSED, char *argv[] TEST_UNUSED)
> +{
> +     if (odp_init_global(NULL, NULL)) {
> +             LOG_ERR("Error: ODP global init failed.\n");
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     if (odp_init_local(ODP_THREAD_WORKER)) {
> +             LOG_ERR("Error: ODP local init failed.\n");
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     int val = test_cuckoo_hash_table();
> +
> +     if (val < 0)
> +             printf("cuckoo hash test fail!!\n");
> +     else
> +             printf("All Tests pass!!\n");
> +
> +     if (odp_term_local()) {
> +             LOG_ERR("Error: ODP local term failed.\n");
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     if (odp_term_global()) {
> +             LOG_ERR("Error: ODP global term failed.\n");
> +             exit(EXIT_FAILURE);
> +     }
> +
> +     return val;
> +}
> +
> diff --git a/include/odp/api/hash.h b/include/odp/api/hash.h
> index 1b2a580..8fd826d 100644
> --- a/include/odp/api/hash.h
> +++ b/include/odp/api/hash.h
> @@ -25,6 +25,20 @@ extern "C" {
>  */
> 
> /**
> + * @typedef odp_hash_function
> + * odp hash function
> + */
> +typedef uint32_t (*odp_hash_function)(const void *key, uint32_t data_len,
> +                                   uint32_t init_val);
> +
> +/**
> + * @typedef odp_hash_cmp_eq_t
> + * odp hash compare function
> + */
> +typedef int (*odp_hash_cmp_eq_t)(const void *key1,
> +                              const void *key2, size_t data_len);
> +
> +/**
> * Calculate CRC-32
> *
> * Calculates CRC-32 over the data. The polynomial is 0x04c11db7.
> @@ -45,7 +59,7 @@ uint32_t odp_hash_crc32(const void *data, uint32_t 
> data_len, uint32_t init_val);
> *
> * @param data       Pointer to data
> * @param data_len   Data length in bytes
> -* @param init_val   CRC generator initialization value
> +* @param init_val   CRC generator initialization nvalue
> *
> * @return CRC32C value
> */
> -- 
> 1.9.1
> 

_______________________________________________
lng-odp mailing list
lng-odp@lists.linaro.org
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to