/*
* Copyright 2015-2018, Intel Corporation
*
* 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 the copyright holder 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.
*/
/*
* cuckoo.c -- implementation of cuckoo hash table
*/
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "cuckoo.h"
#include "out.h"
#define MAX_HASH_FUNCS 2
#define GROWTH_FACTOR 1.2f
#define INITIAL_SIZE 8
#define MAX_INSERTS 8
#define MAX_GROWS 32
struct cuckoo_slot {
uint64_t key;
void *value;
};
struct cuckoo {
size_t size; /* number of hash table slots */
struct cuckoo_slot *tab;
};
static const struct cuckoo_slot null_slot = {0, NULL};
/*
* hash_mod -- (internal) first hash function
*/
static size_t
hash_mod(struct cuckoo *c, uint64_t key)
{
return key % c->size;
}
/*
* hash_mixer -- (internal) second hash function
*
* Based on Austin Appleby MurmurHash3 64-bit finalizer.
*/
static size_t
hash_mixer(struct cuckoo *c, uint64_t key)
{
key ^= key >> 33;
key *= 0xff51afd7ed558ccd;
key ^= key >> 33;
key *= 0xc4ceb9fe1a85ec53;
key ^= key >> 33;
return key % c->size;
}
static size_t
(*hash_funcs[MAX_HASH_FUNCS])(struct cuckoo *c, uint64_t key) = {
hash_mod,
hash_mixer
};
/*
* cuckoo_new -- allocates and initializes cuckoo hash table
*/
struct cuckoo *
cuckoo_new(void)
{
COMPILE_ERROR_ON((size_t)(INITIAL_SIZE * GROWTH_FACTOR)
== INITIAL_SIZE);
struct cuckoo *c = Malloc(sizeof(struct cuckoo));
if (c == NULL) {
ERR("!Malloc");
goto error_cuckoo_malloc;
}
c->size = INITIAL_SIZE;
size_t tab_rawsize = c->size * sizeof(struct cuckoo_slot);
c->tab = Zalloc(tab_rawsize);
if (c->tab == NULL)
goto error_tab_malloc;
return c;
error_tab_malloc:
Free(c);
error_cuckoo_malloc:
return NULL;
}
/*
* cuckoo_delete -- cleanups and deallocates cuckoo hash table
*/
void
cuckoo_delete(struct cuckoo *c)
{
ASSERTne(c, NULL);
Free(c->tab);
Free(c);
}
/*
* cuckoo_insert_try -- (internal) try inserting into the existing hash table
*/
static int
cuckoo_insert_try(struct cuckoo *c, struct cuckoo_slot *src)
{
struct cuckoo_slot srct;
size_t h[MAX_HASH_FUNCS] = {0};
for (int n = 0; n < MAX_INSERTS; ++n) {
for (int i = 0; i < MAX_HASH_FUNCS; ++i) {
h[i] = hash_funcs[i](c, src->key);
if (c->tab[h[i]].value == NULL) {
c->tab[h[i]] = *src;
return 0;
} else if (c->tab[h[i]].key == src->key) {
return EINVAL;
}
}
srct = c->tab[h[0]];
c->tab[h[0]] = *src;
src->key = srct.key;
src->value = srct.value;
}
return EAGAIN;
}
/*
* cuckoo_grow -- (internal) rehashes the table with GROWTH_FACTOR * size
*/
static int
cuckoo_grow(struct cuckoo *c)
{
size_t oldsize = c->size;
struct cuckoo_slot *oldtab = c->tab;
int n;
for (n = 0; n < MAX_GROWS; ++n) {
size_t nsize = (size_t)((float)c->size * GROWTH_FACTOR);
size_t tab_rawsize = nsize * sizeof(struct cuckoo_slot);
c->tab = Zalloc(tab_rawsize);
if (c->tab == NULL) {
c->tab = oldtab;
return ENOMEM;
}
c->size = nsize;
unsigned i;
for (i = 0; i < oldsize; ++i) {
struct cuckoo_slot s = oldtab[i];
if (s.value != NULL && (cuckoo_insert_try(c, &s) != 0))
break;
}
if (i == oldsize)
break;
else
Free(c->tab);
}
if (n == MAX_GROWS) {
c->tab = oldtab;
c->size = oldsize;
return EINVAL;
}
Free(oldtab);
return 0;
}
/*
* cuckoo_insert -- inserts key-value pair into the hash table
*/
int
cuckoo_insert(struct cuckoo *c, uint64_t key, void *value)
{
ASSERTne(c, NULL);
int err;
struct cuckoo_slot src = {key, value};
for (int n = 0; n < MAX_GROWS; ++n) {
if ((err = cuckoo_insert_try(c, &src)) != EAGAIN)
return err;
if ((err = cuckoo_grow(c)) != 0)
return err;
}
return EINVAL;
}
/*
* cuckoo_find_slot -- (internal) finds the hash table slot of key
*/
static struct cuckoo_slot *
cuckoo_find_slot(struct cuckoo *c, uint64_t key)
{
for (int i = 0; i < MAX_HASH_FUNCS; ++i) {
size_t h = hash_funcs[i](c, key);
if (c->tab[h].key == key)
return &c->tab[h];
}
return NULL;
}
/*
* cuckoo_remove -- removes key-value pair from the hash table
*/
void *
cuckoo_remove(struct cuckoo *c, uint64_t key)
{
ASSERTne(c, NULL);
void *ret = NULL;
struct cuckoo_slot *s = cuckoo_find_slot(c, key);
if (s) {
ret = s->value;
*s = null_slot;
}
return ret;
}
/*
* cuckoo_get -- returns the value of a key
*/
void *
cuckoo_get(struct cuckoo *c, uint64_t key)
{
ASSERTne(c, NULL);
struct cuckoo_slot *s = cuckoo_find_slot(c, key);
return s ? s->value : NULL;
}
/*
* cuckoo_get_size -- returns the size of the underlying table, useful for
* calculating load factor and predicting possible rehashes
*/
size_t
cuckoo_get_size(struct cuckoo *c)
{
ASSERTne(c, NULL);
return c->size;
}