blob: fb500605251e103f1d8df4a45435551fe00b1725 [file] [log] [blame]
/*****************************************************************************\
* xahash.c - functions used for arbitrary hash table
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
/*
* Fixed size xahash_t is designed to have all operations with a cost of O(1).
* This requires a good bit of design to achieve the goal.
*
* Fixed size hash table: xahash_table_t
* The fixed count xahash_table_t does 1 xmalloc() call unless there are hash
* collisions. Then it does 1 xmalloc() per each insert with a hash collision.
*
* This is the memory layout of each hash table:
*
* |----------------------------|
* | hash table header | xahash_table_t* and xahash_table_header_t*
* | | body of xahash_table_header_t
* |----------------------------|
* | state blob | _get_state()
* | | xahash_table_header_t->state_blob_bytes
* |----------------------------|
* | |----------------| | _get_fentry(index), fentry_header_t*
* | | entry header | |
* | |----------------| @ index | _get_fentry_blob(fentry_header_t*)
* | | entry blob | |
* | |----------------| | _get_fentry(index,depth) + xahash_table_header_t->bytes_per_entry_blob
* |----------------------------|
*
* Each entry fails down into a linked list on a hash collision:
*
* <-------------- xmalloc() per entry --->
* |----------------| |----------------| |----------------|
* | entry header | | entry header | | entry header |
* | next*| -> | next*| -> | NULL|
* |----------------| |----------------| |----------------|
* | entry blob | | entry blob | | entry blob |
* |----------------| |----------------| |----------------|
*
* Hash collisions are slow and break the O(1) design and should be avoided.
*
*/
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/read_config.h"
#include "src/common/timers.h"
#include "src/common/xahash.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
/* Macro to only include the code inside of ONLY_DEBUG if !NDEBUG */
#ifndef NDEBUG
#define ONLY_DEBUG(...) __VA_ARGS__
#else
#define ONLY_DEBUG(...)
#endif
#define HASH_TABLE_MAGIC 0x131e1aff
#define HASH_FENTRY_MAGIC(index) (((uint32_t) 0xffff0000) ^ ((uint32_t) index))
typedef enum {
HT_FLAG_STATE_MASK = 0xff,
HT_FLAG_INVALID = 0,
HT_FLAG_FIXED_SIZE = SLURM_BIT(0), /* hash table is expected to be fixed and use linked list of entries for hash collisions */
HT_FLAG_DYNAMIC_SIZE = SLURM_BIT(1), /* hash table is expected to resize as needed */
HT_FLAG_INVALID_MAX,
} hash_flags_t;
typedef struct {
ONLY_DEBUG(int magic); /* HASH_TABLE_MAGIC */
hash_flags_t flags;
xahash_func_t hash_func;
xahash_match_func_t match_func;
const char *match_func_string;
xahash_on_insert_func_t on_insert_func;
const char *on_insert_func_string;
xahash_on_free_func_t on_free_func;
const char *on_free_func_string;
size_t state_blob_bytes;
size_t bytes_per_entry_blob;
union {
struct {
size_t count;
} fixed;
} type;
} xahash_table_header_t;
typedef enum {
FENTRY_FLAG_INVALID = 0,
FENTRY_FLAG_UNSET = SLURM_BIT(0), /* entry is not populated */
FENTRY_FLAG_SET = SLURM_BIT(1), /* entry is populated */
FENTRY_FLAG_INVALID_MAX
} fentry_flags_t;
/* Fixed size entry struct */
typedef struct fentry_header_s fentry_header_t;
struct fentry_header_s {
ONLY_DEBUG(int magic); /* HASH_FENTRY_MAGIC % index */
fentry_flags_t flags;
/* next entry in linked list */
fentry_header_t *next;
};
static const struct {
const char *str;
xahash_foreach_control_t value;
} foreach_control_strings[] = {
{ "INVALID", XAHASH_FOREACH_INVALID },
{ "CONTINUE", XAHASH_FOREACH_CONT },
{ "STOP", XAHASH_FOREACH_STOP },
{ "FAIL", XAHASH_FOREACH_FAIL },
{ "INVALID", XAHASH_FOREACH_INVALID_MAX },
};
static void *_get_state(const xahash_table_t *ht);
static fentry_header_t *_get_fentry(xahash_table_t *ht,
xahash_table_header_t *hth, int index);
/* Macro place holders for sanity checks */
#define _is_debug() (false)
#define DEF_DEBUG_TIMER
#define START_DEBUG_TIMER
#define END_DEBUG_TIMER
#define _check_magic(...)
#define _check_fentry_bounds(...)
#define _check_fentry_magic(...)
static bool _is_fixed(const xahash_table_t *ht,
const xahash_table_header_t *hth)
{
_check_magic(ht);
return ((hth->flags & HT_FLAG_STATE_MASK) == HT_FLAG_FIXED_SIZE);
}
static int _fixed_hash_to_index(const xahash_table_t *ht,
const xahash_table_header_t *hth,
const xahash_hash_t hash)
{
xassert(_is_fixed(ht, hth));
return hash % hth->type.fixed.count;
}
static void *_get_state(const xahash_table_t *ht)
{
return ((void *) ht) + sizeof(xahash_table_header_t);
}
static xahash_table_header_t *_get_header(xahash_table_t *ht)
{
xahash_table_header_t *hth = (void *) ht;
xassert(hth->magic == HASH_TABLE_MAGIC);
return hth;
}
static const xahash_table_header_t *_get_header_const(const xahash_table_t *ht)
{
return _get_header((xahash_table_t *) ht);
}
/*
* Get pointer to entry based on index
* WARNING: magic is not checked
* WARNING: entry may not be set
*/
static fentry_header_t *_get_fentry(xahash_table_t *ht,
xahash_table_header_t *hth, int index)
{
fentry_header_t *fe;
void *start_ht = ht;
void *end_hth = start_ht + sizeof(*hth);
void *end_state_blob = end_hth + hth->state_blob_bytes;
const size_t bytes_per_fentry =
(sizeof(fentry_header_t) + hth->bytes_per_entry_blob);
xassert(index >= 0);
xassert(index < hth->type.fixed.count);
fe = end_state_blob + (bytes_per_fentry * index);
_check_fentry_bounds(ht, hth, fe);
return fe;
}
static bool _is_fentry_set(const xahash_table_t *ht,
const xahash_table_header_t *hth,
const fentry_header_t *fe)
{
_check_fentry_magic(ht, hth, fe, -1, -1);
return (fe->flags & FENTRY_FLAG_SET);
}
/* WARNING: does not call _check_fentry_magic() */
static void *_get_fentry_blob_ptr(const xahash_table_t *ht,
const xahash_table_header_t *hth,
const fentry_header_t *fe)
{
void *ptr;
_check_fentry_magic(ht, hth, fe, -1, -1);
ptr = ((void *) fe) + sizeof(*fe);
/* verify pointer is in bounds of the ht's malloc() */
xassert(ptr);
xassert((void *) ptr > (void *) fe);
xassert((void *) ptr >= ((void *) fe) + sizeof(*fe));
return ptr;
}
static void *_get_fentry_blob(const xahash_table_t *ht,
const xahash_table_header_t *hth,
const fentry_header_t *fe)
{
void *blob;
xassert(fe);
_check_fentry_magic(ht, hth, fe, -1, -1);
xassert(_is_fentry_set(ht, hth, fe));
if (!fe || !_is_fentry_set(ht, hth, fe))
return NULL;
blob = _get_fentry_blob_ptr(ht, hth, fe);
xassert(blob);
return blob;
}
static fentry_header_t *_init_fentry(xahash_table_t *ht,
xahash_table_header_t *hth,
fentry_header_t *fe, bool is_new,
const int index, const int depth)
{
if (is_new) {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] initializing fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe);
} else {
_check_fentry_magic(ht, hth, fe, index, depth);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] reinitializing fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe);
}
*fe = (fentry_header_t) {
ONLY_DEBUG(.magic = HASH_FENTRY_MAGIC(index),)
.flags = FENTRY_FLAG_UNSET,
};
_check_fentry_magic(ht, hth, fe, index, depth);
ONLY_DEBUG(memset(_get_fentry_blob_ptr(ht, hth, fe), 0,
hth->bytes_per_entry_blob));
_check_fentry_magic(ht, hth, fe, index, depth);
return fe;
}
static xahash_table_t *_new_fixed_table(xahash_func_t hash_func,
const char *hash_func_string,
xahash_match_func_t match_func,
const char *match_func_string,
xahash_on_insert_func_t on_insert_func,
const char *on_insert_func_string,
xahash_on_free_func_t on_free_func,
const char *on_free_func_string,
const size_t state_bytes,
const size_t bytes_per_entry,
const size_t fixed_table_size)
{
size_t bytes;
xahash_table_t *ht;
xahash_table_header_t *hth;
xassert(fixed_table_size > 0);
xassert(bytes_per_entry > 0);
bytes = sizeof(*hth) + state_bytes +
((sizeof(fentry_header_t) + bytes_per_entry) *
fixed_table_size);
log_flag(DATA, "%s: initializing fixed xahash_table_t with fixed %zu entries and %zu bytes per entry and %zu state bytes for %zu bytes total. Callbacks: hash_func=%s()@0x%"PRIxPTR" match_func=%s()@0x%"PRIxPTR" on_insert_func=%s()@0x%"PRIxPTR" on_free_func=%s()@0x%"PRIxPTR,
__func__, fixed_table_size, bytes_per_entry, state_bytes, bytes,
hash_func_string, (uintptr_t) hash_func, match_func_string,
(uintptr_t) match_func, on_insert_func_string,
(uintptr_t) on_insert_func, on_free_func_string,
(uintptr_t) on_free_func);
hth = ht = xmalloc_nz(bytes);
*hth = (xahash_table_header_t){
ONLY_DEBUG(.magic = HASH_TABLE_MAGIC,)
.flags = HT_FLAG_FIXED_SIZE,
.hash_func = hash_func,
.match_func = match_func,
.match_func_string = match_func_string,
.on_insert_func = on_insert_func,
.on_insert_func_string = on_insert_func_string,
.on_free_func = on_free_func,
.on_free_func_string = on_free_func_string,
.state_blob_bytes = state_bytes,
.bytes_per_entry_blob = bytes_per_entry,
.type.fixed = {
.count = fixed_table_size,
}
};
for (int i = 0; i < hth->type.fixed.count; i++)
(void) _init_fentry(ht, hth, _get_fentry(ht, hth, i), true, i,
0);
ONLY_DEBUG(memset(_get_state(ht), 0, state_bytes));
return ht;
}
extern xahash_table_t *xahash_new_table_funcname(
xahash_func_t hash_func, const char *hash_func_string,
xahash_match_func_t match_func, const char *match_func_string,
xahash_on_insert_func_t on_insert_func,
const char *on_insert_func_string, xahash_on_free_func_t on_free_func,
const char *on_free_func_string, const size_t state_bytes,
const size_t bytes_per_entry, const size_t fixed_table_size)
{
if (fixed_table_size > 0)
return _new_fixed_table(hash_func, hash_func_string, match_func,
match_func_string, on_insert_func,
on_insert_func_string, on_free_func,
on_free_func_string, state_bytes,
bytes_per_entry, fixed_table_size);
/* TODO: dynamic sizing not implemented yet */
fatal_abort("should never execute");
}
static void _free_fentry(xahash_table_t *ht, xahash_table_header_t *hth,
int index, int depth, fentry_header_t *fe,
fentry_header_t *parent)
{
fentry_header_t *next = fe->next;
if (next) {
_check_fentry_magic(ht, hth, fe->next, index, (depth + 1));
xassert(parent != next);
xassert(next != fe);
}
if (hth->on_free_func && _is_fentry_set(ht, hth, fe)) {
log_flag_hex(DATA, _get_fentry_blob_ptr(ht, hth, fe),
hth->bytes_per_entry_blob,
"%s: [hashtable@0x%"PRIxPTR"] calling %s()@0x%"PRIxPTR" for fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, hth->on_free_func_string,
(uintptr_t) hth->on_free_func, index, depth,
(uintptr_t) fe);
hth->on_free_func(_get_fentry_blob(ht, hth, fe),
_get_state(ht));
}
if (parent) {
log_flag_hex(DATA, _get_fentry_blob_ptr(ht, hth, fe),
hth->bytes_per_entry_blob,
"%s: [hashtable@0x%"PRIxPTR"] dropping linked fentry[%d][%d]@0x%"PRIxPTR" -> fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, index, (depth - 1),
(uintptr_t) parent, index, depth, (uintptr_t) fe);
xassert(parent->next == fe);
xassert(depth > 0);
_check_fentry_magic(ht, hth, parent, index, (depth - 1));
/* unlink fe from list */
parent->next = next;
ONLY_DEBUG(fe->magic = ~HASH_FENTRY_MAGIC(index));
xfree(fe);
_check_fentry_magic(ht, hth, parent, index, (depth - 1));
if (next)
_check_fentry_magic(ht, hth, next, index, depth);
} else {
xassert(!depth);
log_flag_hex(DATA, _get_fentry_blob_ptr(ht, hth, fe),
hth->bytes_per_entry_blob,
"%s: [hashtable@0x%"PRIxPTR"] releasing fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe);
(void) _init_fentry(ht, hth, fe, false, index, depth);
fe->next = next;
}
}
static void _free_fixed_table_members(xahash_table_t *ht,
xahash_table_header_t *hth)
{
for (int i = 0; i < hth->type.fixed.count; i++) {
fentry_header_t *fe = _get_fentry(ht, hth, i);
_check_fentry_magic(ht, hth, fe, i, 0);
while (fe->next) {
_check_fentry_magic(ht, hth, fe, i, 1);
_free_fentry(ht, hth, i, 1, fe->next, fe);
}
_free_fentry(ht, hth, i, 0, fe, NULL);
}
}
extern void xahash_free_table(xahash_table_t *ht)
{
xahash_table_header_t *hth;
_check_magic(ht);
if (!ht)
return;
hth = _get_header(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request free hashtable",
__func__, (uintptr_t) ht);
if (_is_fixed(ht, hth))
_free_fixed_table_members(ht, hth);
ONLY_DEBUG(hth->magic = ~HASH_TABLE_MAGIC);
xfree(ht);
}
extern void *xahash_get_state_ptr(xahash_table_t *ht)
{
DEF_DEBUG_TIMER;
xahash_table_header_t *hth = (void *) ht;
void *ptr;
START_DEBUG_TIMER;
_check_magic(ht);
ptr = _get_state(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request table state=0x%"PRIxPTR"[%zu]",
__func__, (uintptr_t) ht, (uintptr_t) ptr,
hth->state_blob_bytes);
END_DEBUG_TIMER;
return ptr;
}
static bool _match_fixed_entry(xahash_table_t *ht, xahash_table_header_t *hth,
const xahash_hash_t hash, const void *key,
const size_t key_bytes, fentry_header_t *fe,
const int index, const int depth)
{
_check_fentry_magic(ht, hth, fe, index, depth);
if (!_is_fentry_set(ht, hth, fe)) {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] skip unset fentry[%d][%d]@0x%"PRIxPTR" != 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe, (uintptr_t) key, key_bytes,
hash);
return false;
}
if (hth->match_func(_get_fentry_blob(ht, hth, fe), key, key_bytes,
_get_state(ht))) {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] %s()@0x%"PRIxPTR"=true accepted fentry[%d][%d]@0x%"PRIxPTR" == 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, hth->match_func_string,
(uintptr_t) hth->match_func, index, depth,
(uintptr_t) fe, (uintptr_t) key, key_bytes, hash);
return true;
} else {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] %s()@0x%"PRIxPTR"=false rejected fentry[%d][%d]@0x%"PRIxPTR" != 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, hth->match_func_string,
(uintptr_t) hth->match_func, index, depth,
(uintptr_t) fe, (uintptr_t) key, key_bytes, hash);
return false;
}
}
static fentry_header_t *_find_fixed_entry(xahash_table_t *ht,
xahash_table_header_t *hth,
const xahash_hash_t hash,
const void *key,
const size_t key_bytes)
{
const int index = _fixed_hash_to_index(ht, hth, hash);
fentry_header_t *fe = _get_fentry(ht, hth, index);
int depth = 0;
_check_fentry_magic(ht, hth, fe, index, depth);
do {
if (_match_fixed_entry(ht, hth, hash, key, key_bytes, fe, index,
depth))
return fe;
depth++;
} while ((fe = fe->next));
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] unable to find fentry for 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, (uintptr_t) key, key_bytes,
hash);
return NULL;
}
static const fentry_header_t *_find_fixed_entry_const(
const xahash_table_t *ht, const xahash_table_header_t *hth,
const void *key, const size_t key_bytes)
{
const xahash_hash_t hash =
hth->hash_func(key, key_bytes, _get_state(ht));
/* de-constify and then return as const */
return _find_fixed_entry((xahash_table_t *) ht,
(xahash_table_header_t *) hth, hash, key,
key_bytes);
}
static void *_find_fixed_entry_blob(const xahash_table_t *ht,
const xahash_table_header_t *hth,
const void *key, const size_t key_bytes)
{
const fentry_header_t *fe =
_find_fixed_entry_const(ht, hth, key, key_bytes);
if (!fe || !_is_fentry_set(ht, hth, fe))
return NULL;
return _get_fentry_blob(ht, hth, fe);
}
extern void *xahash_find_entry(const xahash_table_t *ht, const void *key,
const size_t key_bytes)
{
DEF_DEBUG_TIMER;
const xahash_table_header_t *hth;
void *ptr = NULL;
_check_magic(ht);
if (!ht || !key || !key_bytes)
return NULL;
START_DEBUG_TIMER;
hth = _get_header_const(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request find entry for 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, (uintptr_t) key, key_bytes,
hth->hash_func(key, key_bytes, _get_state(ht)));
if (_is_fixed(ht, hth))
ptr = _find_fixed_entry_blob(ht, hth, key, key_bytes);
else
fatal_abort("should never execute");
END_DEBUG_TIMER;
return ptr;
}
static fentry_header_t *_append_fentry(xahash_table_t *ht,
xahash_table_header_t *hth,
fentry_header_t *fe,
const xahash_hash_t hash,
const int index, int *depth_ptr)
{
xassert(_is_fentry_set(ht, hth, fe));
/* Find unset entry if one is already in chain */
while (fe) {
if (!_is_fentry_set(ht, hth, fe)) {
/* found empty entry so return it instead */
return fe;
}
if (!fe->next) {
size_t bytes = sizeof(*fe);
bytes += hth->bytes_per_entry_blob;
fe->next = xmalloc_nz(bytes);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] new linked fentry[%d][%d]@0x%"PRIxPTR" -> fentry[%d][%d]@0x%"PRIxPTR"=#0x%x",
__func__, (uintptr_t) ht, index, *depth_ptr,
(uintptr_t) fe, index, (*depth_ptr + 1),
(uintptr_t) fe->next, hash);
(*depth_ptr)++;
return _init_fentry(ht, hth, fe->next, true, index,
*depth_ptr);
}
fe = fe->next;
(*depth_ptr)++;
}
fatal_abort("should never execute");
}
static void *_insert_fixed_entry(xahash_table_t *ht, xahash_table_header_t *hth,
const void *key, const size_t key_bytes)
{
int depth = -1, index = -1;
const xahash_hash_t hash =
hth->hash_func(key, key_bytes, _get_state(ht));
fentry_header_t *fe = _find_fixed_entry(ht, hth, hash, key, key_bytes);
if (fe) {
xassert(_is_fentry_set(ht, hth, fe));
log_flag_hex(DATA, _get_fentry_blob_ptr(ht, hth, fe),
hth->bytes_per_entry_blob,
"%s: [hashtable@0x%"PRIxPTR"] ignoring duplicate insert on existing fentry@0x%"PRIxPTR,
__func__, (uintptr_t) ht, (uintptr_t) fe);
return _get_fentry_blob(ht, hth, fe);
}
/* not found: find and place a new entry */
index = _fixed_hash_to_index(ht, hth, hash);
depth = 0;
fe = _get_fentry(ht, hth, index);
_check_fentry_magic(ht, hth, fe, index, depth);
if (_is_fentry_set(ht, hth, fe)) {
/* need to add to linked list */
fe = _append_fentry(ht, hth, fe, hash, index, &depth);
}
/* check for clobbering */
xassert(fe->flags == FENTRY_FLAG_UNSET);
fe->flags = FENTRY_FLAG_SET;
if (hth->on_insert_func) {
hth->on_insert_func(_get_fentry_blob(ht, hth, fe), key,
key_bytes, _get_state(ht));
log_flag_hex(DATA, _get_fentry_blob_ptr(ht, hth, fe),
hth->bytes_per_entry_blob,
"%s: [hashtable@0x%"PRIxPTR"] inserted after %s()@0x%"PRIxPTR" for fentry[%d][%d]@0x%"PRIxPTR"=#0x%x",
__func__, (uintptr_t) ht,
hth->on_insert_func_string,
(uintptr_t) hth->on_insert_func, index, depth,
(uintptr_t) fe, hash);
} else {
log_flag(DATA,
"%s: [hashtable@0x%" PRIxPTR
"] inserted fentry[%d][%d]@0x%" PRIxPTR "=#0x%x",
__func__, (uintptr_t) ht, index, depth, (uintptr_t) fe,
hash);
}
return _get_fentry_blob(ht, hth, fe);
}
extern void *xahash_insert_entry(xahash_table_t *ht, const void *key,
const size_t key_bytes)
{
DEF_DEBUG_TIMER;
xahash_table_header_t *hth;
void *ptr = NULL;
xassert(key);
xassert(key_bytes > 0);
_check_magic(ht);
if (!ht || !key || !key_bytes)
return NULL;
START_DEBUG_TIMER;
hth = _get_header(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request insert entry for 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, (uintptr_t) key, key_bytes,
hth->hash_func(key, key_bytes, _get_state(ht)));
if (_is_fixed(ht, hth))
ptr = _insert_fixed_entry(ht, hth, key, key_bytes);
else
fatal_abort("should never execute");
END_DEBUG_TIMER;
return ptr;
}
static bool _find_and_free_fentry(xahash_table_t *ht,
xahash_table_header_t *hth, const void *key,
const size_t key_bytes)
{
const xahash_hash_t hash =
hth->hash_func(key, key_bytes, _get_state(ht));
const int index = _fixed_hash_to_index(ht, hth, hash);
fentry_header_t *fe = _get_fentry(ht, hth, index);
fentry_header_t *parent = NULL;
int depth = 0;
_check_fentry_magic(ht, hth, fe, index, -1);
while (fe) {
if (_is_fentry_set(ht, hth, fe)) {
if (hth->match_func(_get_fentry_blob(ht, hth, fe), key,
key_bytes, _get_state(ht))) {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] matched fentry[%d][%d]@0x%"PRIxPTR" == 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe, (uintptr_t) key,
key_bytes, hash);
break;
} else {
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] match_func rejected fentry[%d][%d]@0x%"PRIxPTR" != 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, index, depth,
(uintptr_t) fe, (uintptr_t) key,
key_bytes, hash);
}
}
parent = fe;
fe = fe->next;
depth++;
}
if (!fe)
return false;
_free_fentry(ht, hth, index, depth, fe, parent);
return true;
}
extern bool xahash_free_entry(xahash_table_t *ht, const void *key,
const size_t key_bytes)
{
DEF_DEBUG_TIMER;
xahash_table_header_t *hth;
bool rc = false;
_check_magic(ht);
xassert(key);
xassert(key_bytes > 0);
if (!ht || !key || !key_bytes)
return false;
START_DEBUG_TIMER;
hth = _get_header(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request free entry for 0x%"PRIxPTR"[%zu]=#0x%x",
__func__, (uintptr_t) ht, (uintptr_t) key, key_bytes,
hth->hash_func(key, key_bytes, _get_state(ht)));
if (_is_fixed(ht, hth)) {
rc = _find_and_free_fentry(ht, hth, key, key_bytes);
} else {
fatal_abort("should never execute");
}
END_DEBUG_TIMER;
return rc;
}
static const char *_foreach_control_string(xahash_foreach_control_t control)
{
for (int i = 0; i < ARRAY_SIZE(foreach_control_strings); i++)
if (foreach_control_strings[i].value == control)
return foreach_control_strings[i].str;
fatal_abort("should never execute");
}
static xahash_foreach_control_t _foreach_fentry_entry(
xahash_table_t *ht, xahash_table_header_t *hth,
xahash_foreach_func_t callback, const char *callback_string, void *arg,
fentry_header_t *fe, const int index, const int depth)
{
xahash_foreach_control_t rc;
rc = callback(_get_fentry_blob(ht, hth, fe), _get_state(ht), arg);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] called after %s()@0x%"PRIxPTR"=%s for fentry[%d][%d]@0x%"PRIxPTR,
__func__, (uintptr_t) ht, callback_string,
(uintptr_t) callback, _foreach_control_string(rc), index,
depth, (uintptr_t) fe);
return rc;
}
static int _foreach_fentry(xahash_table_t *ht, xahash_table_header_t *hth,
xahash_foreach_func_t callback,
const char *callback_string, void *arg)
{
uint32_t count = 0;
for (uint32_t i = 0; i < hth->type.fixed.count; i++) {
uint32_t depth = 0;
fentry_header_t *fe = _get_fentry(ht, hth, i);
do {
xahash_foreach_control_t rc;
if (!_is_fentry_set(ht, hth, fe))
continue;
count++;
rc = _foreach_fentry_entry(ht, hth, callback,
callback_string, arg, fe, i,
depth);
xassert(rc > XAHASH_FOREACH_INVALID);
xassert(rc < XAHASH_FOREACH_INVALID_MAX);
switch (rc) {
case XAHASH_FOREACH_CONT:
/* do nothing */
break;
case XAHASH_FOREACH_STOP:
return count;
case XAHASH_FOREACH_FAIL:
return (count * -1);
case XAHASH_FOREACH_INVALID:
case XAHASH_FOREACH_INVALID_MAX:
fatal_abort("should never execute");
}
} while (depth++, (fe = fe->next));
}
return count;
}
extern int xahash_foreach_entry_funcname(xahash_table_t *ht,
xahash_foreach_func_t callback,
const char *callback_string, void *arg)
{
DEF_DEBUG_TIMER;
xahash_table_header_t *hth;
int rc = 0;
_check_magic(ht);
if (!ht)
return rc;
START_DEBUG_TIMER;
hth = _get_header(ht);
log_flag(DATA, "%s: [hashtable@0x%"PRIxPTR"] request foreach func:%s()@0x%"PRIxPTR" arg:0x%"PRIxPTR,
__func__, (uintptr_t) ht, callback_string,
(uintptr_t) callback, (uintptr_t) arg);
if (_is_fixed(ht, hth))
rc = _foreach_fentry(ht, hth, callback, callback_string, arg);
else
fatal_abort("should never execute");
END_DEBUG_TIMER;
return rc;
}