Table of Contents
- src/n_hash.c
- Functions
- Attributes
- Defines
- Detailed Description
- Functions Documentation
- function getblock64
- function getblock32
- function fmix64
- function fmix32
- function _ht_search_trie_helper
- function _ht_search_trie
- function _ht_search
- function _ht_remove_trie
- function _ht_remove
- function _ht_put_string_trie
- function _ht_put_string_ptr_trie
- function _ht_put_string_ptr
- function _ht_put_string
- function _ht_put_ptr_trie
- function _ht_put_ptr
- function _ht_put_int_trie
- function _ht_put_int
- function _ht_put_double_trie
- function _ht_put_double
- function _ht_print_trie_helper
- function _ht_print_trie
- function _ht_print
- function _ht_node_destroy
- function _ht_new_string_ptr_node
- function _ht_new_string_node
- function _ht_new_seed
- function _ht_new_ptr_node
- function _ht_new_node_trie
- function _ht_new_node
- function _ht_new_int_node
- function _ht_new_double_node
- function _ht_mix
- function _ht_key_init
- function _ht_is_leaf_node_trie
- function _ht_hash_value
- function _ht_get_string_trie
- function _ht_get_string
- function _ht_get_ptr_trie
- function _ht_get_ptr
- function _ht_get_node_trie
- function _ht_get_node
- function _ht_get_int_trie
- function _ht_get_int
- function _ht_get_double_trie
- function _ht_get_double
- function _ht_find_longest_prefix_trie
- function _ht_depth_first_search
- function _ht_check_trie_divergence
- function _empty_ht_trie
- function _empty_ht
- function _destroy_ht_trie
- function _destroy_ht
- Attributes Documentation
- Macros Documentation
- Source code
src/n_hash.c
Hash functions and table. More...
Functions
| Name | |
|---|---|
| uint64_t | getblock64(const uint64_t * p, const size_t i) Block read - modified from murmur's author, ajusted byte endianess. |
| uint32_t | getblock32(const uint32_t * p, const size_t i) Block read - modified from murmur's author, ajusted byte endianess. |
| uint64_t | fmix64(uint64_t k) Finalization mix - force all bits of a hash block to avalanche (from murmur's author). |
| uint32_t | fmix32(uint32_t h) Finalization mix - force all bits of a hash block to avalanche (from murmur's author). |
| void | _ht_search_trie_helper(LIST * results, HASH_NODE * node, int(*)(HASH_NODE *node) node_is_matching) Recursive function to search tree's keys and apply a matching func to put results in the list. |
| LIST * | _ht_search_trie(HASH_TABLE * table, int(*)(HASH_NODE *node) node_is_matching) Search tree's keys and apply a matching func to put results in the list. |
| LIST * | _ht_search(HASH_TABLE * table, int(*)(HASH_NODE *node) node_is_matching) Search hash table's keys and apply a matching func to put results in the list. |
| int | _ht_remove_trie(HASH_TABLE * table, const char * key) Remove a key from a trie table and destroy the node. |
| int | _ht_remove(HASH_TABLE * table, const char * key) Remove a key from a hash table. |
| int | _ht_put_string_trie(HASH_TABLE * table, const char * key, char * string) put a duplicate of the string value with given key in the targeted hash table [TRIE HASH TABLE) |
| int | _ht_put_string_ptr_trie(HASH_TABLE * table, const char * key, char * string) put a pointer to the string value with given key in the targeted hash table [TRIE HASH TABLE) |
| int | _ht_put_string_ptr(HASH_TABLE * table, const char * key, char * string) put a null terminated char *string with given key in the targeted hash table (pointer) |
| int | _ht_put_string(HASH_TABLE * table, const char * key, char * string) put a null terminated char *string with given key in the targeted hash table (copy of string) |
| int | _ht_put_ptr_trie(HASH_TABLE * table, const char * key, void * ptr, void(*)(void *ptr) destructor, void ()(void *ptr) duplicator) put a pointer to the string value with given key in the targeted hash table [TRIE HASH TABLE) |
| int | _ht_put_ptr(HASH_TABLE * table, const char * key, void * ptr, void(*)(void *ptr) destructor, void ()(void *ptr) duplicator) put a pointer value with given key in the targeted hash table |
| int | _ht_put_int_trie(HASH_TABLE * table, const char * key, int64_t value) put an integral value with given key in the targeted hash table [TRIE HASH TABLE) |
| int | _ht_put_int(HASH_TABLE * table, const char * key, int64_t value) put an integral value with given key in the targeted hash table [CLASSIC HASH TABLE) |
| int | _ht_put_double_trie(HASH_TABLE * table, const char * key, double value) put a double value with given key in the targeted hash table [TRIE HASH TABLE) |
| int | _ht_put_double(HASH_TABLE * table, const char * key, double value) put a double value with given key in the targeted hash table |
| void | _ht_print_trie_helper(HASH_TABLE * table, HASH_NODE * node) Recursive function to print trie tree's keys and values. |
| void | _ht_print_trie(HASH_TABLE * table) Generic print func call for trie trees. |
| void | _ht_print(HASH_TABLE * table) Generic print func call for classic hash tables. |
| void | _ht_node_destroy(void * node) destroy a HASH_NODE by first calling the HASH_NODE destructor |
| HASH_NODE * | _ht_new_string_ptr_node(HASH_TABLE * table, const char * key, char * value) node creation, HASH_CLASSIC mode, pointer to string value |
| HASH_NODE * | _ht_new_string_node(HASH_TABLE * table, const char * key, const char * value) node creation, HASH_CLASSIC mode, strdup of value |
| size_t | _ht_new_seed(void ) |
| HASH_NODE * | _ht_new_ptr_node(HASH_TABLE * table, const char * key, void * value, void(*)(void *ptr) destructor, void ()(void *ptr) duplicator) node creation, HASH_CLASSIC mode, pointer to string value |
| HASH_NODE * | _ht_new_node_trie(HASH_TABLE * table, const char key) node creation, HASH_CLASSIC mode |
| HASH_NODE * | _ht_new_node(const HASH_TABLE * table, const char * key) node creation, HASH_CLASSIC mode |
| HASH_NODE * | _ht_new_int_node(HASH_TABLE * table, const char * key, int64_t value) node creation, HASH_CLASSIC mode |
| HASH_NODE * | _ht_new_double_node(HASH_TABLE * table, const char * key, double value) node creation, HASH_CLASSIC mode |
| uint64_t | _ht_mix(uint64_t x) |
| void | _ht_key_init(void ) |
| int | _ht_is_leaf_node_trie(HASH_TABLE * table, const char * key) Search a key and tell if it's holding a value (leaf). |
| HASH_VALUE | _ht_hash_value(const HASH_TABLE * table, const char * key, size_t key_len) compute a key's hash value with a correctly sized output buffer |
| int | _ht_get_string_trie(HASH_TABLE * table, const char * key, char ** val) Retrieve an char *string value in the hash table, at the given key. |
| int | _ht_get_string(HASH_TABLE * table, const char * key, char ** val) Retrieve a char *string value in the hash table, at the given key. |
| int | _ht_get_ptr_trie(HASH_TABLE * table, const char * key, void ** val) Retrieve a pointer value in the hash table, at the given key. |
| int | _ht_get_ptr(HASH_TABLE * table, const char * key, void ** val) Retrieve a pointer value in the hash table, at the given key. |
| HASH_NODE * | _ht_get_node_trie(HASH_TABLE * table, const char * key) retrieve a HASH_NODE at key from table |
| HASH_NODE * | _ht_get_node(HASH_TABLE * table, const char * key) return the associated key's node inside the hash_table |
| int | _ht_get_int_trie(HASH_TABLE * table, const char * key, int64_t * val) Retrieve an integral value in the hash table, at the given key. |
| int | _ht_get_int(HASH_TABLE * table, const char * key, int64_t * val) Retrieve an integral value in the hash table, at the given key. |
| int | _ht_get_double_trie(HASH_TABLE * table, const char * key, double * val) Retrieve an double value in the hash table, at the given key. |
| int | _ht_get_double(HASH_TABLE * table, const char * key, double * val) Retrieve a double value in the hash table, at the given key. |
| char * | _ht_find_longest_prefix_trie(HASH_TABLE * table, const char * key) find the longest prefix string that is not the current key |
| int | _ht_depth_first_search(HASH_NODE * node, LIST * results) recursive, helper for ht_get_completion_list, get the list of leaf starting from node |
| size_t | _ht_check_trie_divergence(HASH_TABLE * table, const char * key) check and return branching index in key if any |
| int | _empty_ht_trie(HASH_TABLE * table) Empty a TRIE hash table. |
| int | _empty_ht(HASH_TABLE * table) Empty a hash table (CLASSIC mode). |
| int | _destroy_ht_trie(HASH_TABLE ** table) Free and set the table to NULL (TRIE mode). |
| int | _destroy_ht(HASH_TABLE ** table) Free and set the table to NULL. |
Attributes
| Name | |
|---|---|
| uint64_t | _ht_table_counter |
| uint64_t | _ht_process_key |
| pthread_once_t | _ht_key_once |
| pthread_mutex_t | _ht_counter_lock |
Defines
| Name | |
|---|---|
| ROTL64(x, r) 64 bit rotate left |
|
| ROTL32(x, r) 32 bit rotate left |
|
| BYTESWAP64(x) 32 bits bytes swap |
|
| BYTESWAP32(x) 32 bits bytes swap |
|
| BIG_CONSTANT(x) max unsigned long long |
Detailed Description
Hash functions and table.
Author: Castagnier Mickael
Version: 2.0
Date: 16/03/2015
Functions Documentation
function getblock64
uint64_t getblock64(
const uint64_t * p,
const size_t i
)
Block read - modified from murmur's author, ajusted byte endianess.
Parameters:
- p value
- i position
Return: value at position inside block
function getblock32
uint32_t getblock32(
const uint32_t * p,
const size_t i
)
Block read - modified from murmur's author, ajusted byte endianess.
Parameters:
- p value
- i position
Return: value at position inside block
function fmix64
uint64_t fmix64(
uint64_t k
)
Finalization mix - force all bits of a hash block to avalanche (from murmur's author).
Parameters:
- k value
Return: mixed value
function fmix32
uint32_t fmix32(
uint32_t h
)
Finalization mix - force all bits of a hash block to avalanche (from murmur's author).
Parameters:
- h value
Return: mixed value
function _ht_search_trie_helper
void _ht_search_trie_helper(
LIST * results,
HASH_NODE * node,
int(*)(HASH_NODE *node) node_is_matching
)
Recursive function to search tree's keys and apply a matching func to put results in the list.
Parameters:
- results targeted and initialized LIST in which the matching nodes will be put
- node targeted starting trie node
- node_is_matching pointer to a matching function to use
function _ht_search_trie
LIST * _ht_search_trie(
HASH_TABLE * table,
int(*)(HASH_NODE *node) node_is_matching
)
Search tree's keys and apply a matching func to put results in the list.
Parameters:
- table targeted table
- node_is_matching pointer to a matching function to use
Return: NULL or a LIST *list of HASH_NODE *elements
function _ht_search
LIST * _ht_search(
HASH_TABLE * table,
int(*)(HASH_NODE *node) node_is_matching
)
Search hash table's keys and apply a matching func to put results in the list.
Parameters:
- table targeted table
- node_is_matching pointer to a matching function to use
Return: NULL or a LIST *list of HASH_NODE *elements
function _ht_remove_trie
int _ht_remove_trie(
HASH_TABLE * table,
const char * key
)
Remove a key from a trie table and destroy the node.
Parameters:
- table targeted tabled
- key the node key to kill
Return: TRUE or FALSE
function _ht_remove
int _ht_remove(
HASH_TABLE * table,
const char * key
)
Remove a key from a hash table.
Parameters:
- table targeted hash table
- key Key to remove
Return: TRUE or FALSE.
function _ht_put_string_trie
int _ht_put_string_trie(
HASH_TABLE * table,
const char * key,
char * string
)
put a duplicate of the string value with given key in the targeted hash table [TRIE HASH TABLE)
Parameters:
- table targeted hash table
- key associated value's key
- string string value to put (copy)
Return: TRUE or FALSE
function _ht_put_string_ptr_trie
int _ht_put_string_ptr_trie(
HASH_TABLE * table,
const char * key,
char * string
)
put a pointer to the string value with given key in the targeted hash table [TRIE HASH TABLE)
Parameters:
- table targeted hash table
- key associated value's key
- string string value to put (pointer)
Return: TRUE or FALSE
function _ht_put_string_ptr
int _ht_put_string_ptr(
HASH_TABLE * table,
const char * key,
char * string
)
put a null terminated char *string with given key in the targeted hash table (pointer)
Parameters:
- table targeted hash table
- key Associated value's key
- string The string to put
Return: TRUE or FALSE
function _ht_put_string
int _ht_put_string(
HASH_TABLE * table,
const char * key,
char * string
)
put a null terminated char *string with given key in the targeted hash table (copy of string)
Parameters:
- table targeted hash table
- key Associated value's key
- string string value to put (will be strdup'ed)
Return: TRUE or FALSE
function _ht_put_ptr_trie
int _ht_put_ptr_trie(
HASH_TABLE * table,
const char * key,
void * ptr,
void(*)(void *ptr) destructor,
void *(*)(void *ptr) duplicator
)
put a pointer to the string value with given key in the targeted hash table [TRIE HASH TABLE)
Parameters:
- table targeted hash table
- key associated value's key
- ptr pointer value to put
- destructor the destructor func for ptr or NULL
- duplicator the duplicate func for ptr or NULL
Return: TRUE or FALSE
function _ht_put_ptr
int _ht_put_ptr(
HASH_TABLE * table,
const char * key,
void * ptr,
void(*)(void *ptr) destructor,
void *(*)(void *ptr) duplicator
)
put a pointer value with given key in the targeted hash table
Parameters:
- table targeted hash table
- key Associated value's key
- ptr pointer value to put
- destructor Pointer to the ptr type destructor function. Leave to NULL if there isn't
- duplicator Pointer to the ptr type duplicator function. Leave to NULL if there isn't
Return: TRUE or FALSE
function _ht_put_int_trie
int _ht_put_int_trie(
HASH_TABLE * table,
const char * key,
int64_t value
)
put an integral value with given key in the targeted hash table [TRIE HASH TABLE)
Parameters:
- table targeted hash table
- key associated value's key
- value integral value to put
Return: TRUE or FALSE
function _ht_put_int
int _ht_put_int(
HASH_TABLE * table,
const char * key,
int64_t value
)
put an integral value with given key in the targeted hash table [CLASSIC HASH TABLE)
Parameters:
- table targeted table
- key associated value's key
- value integral value to put
Return: TRUE or FALSE
function _ht_put_double_trie
int _ht_put_double_trie(
HASH_TABLE * table,
const char * key,
double value
)
put a double value with given key in the targeted hash table [TRIE HASH TABLE)
Parameters:
- table targeted hash table
- key associated value's key
- value double value to put
Return: TRUE or FALSE
function _ht_put_double
int _ht_put_double(
HASH_TABLE * table,
const char * key,
double value
)
put a double value with given key in the targeted hash table
Parameters:
- table targeted hash table
- key associated value's key
- value double value to put
Return: TRUE or FALSE
function _ht_print_trie_helper
void _ht_print_trie_helper(
HASH_TABLE * table,
HASH_NODE * node
)
Recursive function to print trie tree's keys and values.
Parameters:
- table targeted hash table
- node current node
function _ht_print_trie
void _ht_print_trie(
HASH_TABLE * table
)
Generic print func call for trie trees.
Parameters:
- table targeted hash table
function _ht_print
void _ht_print(
HASH_TABLE * table
)
Generic print func call for classic hash tables.
Parameters:
- table targeted hash table
function _ht_node_destroy
void _ht_node_destroy(
void * node
)
destroy a HASH_NODE by first calling the HASH_NODE destructor
Parameters:
- node The node to kill
function _ht_new_string_ptr_node
HASH_NODE * _ht_new_string_ptr_node(
HASH_TABLE * table,
const char * key,
char * value
)
node creation, HASH_CLASSIC mode, pointer to string value
Parameters:
- table targeted table
- key key of new node
- value char *value of key
Return: NULL or a new HASH_NODE *
function _ht_new_string_node
HASH_NODE * _ht_new_string_node(
HASH_TABLE * table,
const char * key,
const char * value
)
node creation, HASH_CLASSIC mode, strdup of value
Parameters:
- table targeted table
- key key of new node
- value char *value of key
Return: NULL or a new HASH_NODE *
function _ht_new_seed
static size_t _ht_new_seed(
void
)
function _ht_new_ptr_node
HASH_NODE * _ht_new_ptr_node(
HASH_TABLE * table,
const char * key,
void * value,
void(*)(void *ptr) destructor,
void *(*)(void *ptr) duplicator
)
node creation, HASH_CLASSIC mode, pointer to string value
Parameters:
- table targeted table
- key key of new node
- value pointer data of key
- destructor function pointer to a destructor of value type
- duplicator function pointer to a duplicator of value type (or NULL)
Return: NULL or a new HASH_NODE *
function _ht_new_node_trie
HASH_NODE * _ht_new_node_trie(
HASH_TABLE * table,
const char key
)
node creation, HASH_CLASSIC mode
Parameters:
- table targeted table
- key key of new node
Return: NULL or a new HASH_NODE *
function _ht_new_node
HASH_NODE * _ht_new_node(
const HASH_TABLE * table,
const char * key
)
node creation, HASH_CLASSIC mode
Parameters:
- table targeted table
- key key of new node
Return: NULL or a new HASH_NODE *
function _ht_new_int_node
HASH_NODE * _ht_new_int_node(
HASH_TABLE * table,
const char * key,
int64_t value
)
node creation, HASH_CLASSIC mode
Parameters:
- table targeted table
- key key of new node
- value int value of key
Return: NULL or a new HASH_NODE *
function _ht_new_double_node
HASH_NODE * _ht_new_double_node(
HASH_TABLE * table,
const char * key,
double value
)
node creation, HASH_CLASSIC mode
Parameters:
- table targeted table
- key key of new node
- value double value of key
Return: NULL or a new HASH_NODE *
function _ht_mix
static uint64_t _ht_mix(
uint64_t x
)
function _ht_key_init
static void _ht_key_init(
void
)
function _ht_is_leaf_node_trie
int _ht_is_leaf_node_trie(
HASH_TABLE * table,
const char * key
)
Search a key and tell if it's holding a value (leaf).
Parameters:
- table targeted hash table
- key key verify
Return: 1 or 0
function _ht_hash_value
static HASH_VALUE _ht_hash_value(
const HASH_TABLE * table,
const char * key,
size_t key_len
)
compute a key's hash value with a correctly sized output buffer
Parameters:
- table targeted table, for its seed
- key the key to hash
- key_len length of key
Return: the key's hash value
Both MurmurHash 128-bit variants write exactly 16 bytes, whatever HASH_VALUE happens to measure:
64-bit: MurmurHash3_x64_128 -> 2 x uint64_t, and HASH_VALUE[2] is 16 bytes 32-bit: MurmurHash3_x86_128 -> 4 x uint32_t, but HASH_VALUE is size_t, so a HASH_VALUE[2] is only 8 bytes
Passing a HASH_VALUE[2] therefore overflowed the stack by 8 bytes on every hash insert, lookup and remove of every 32-bit build, while 64-bit builds fitted exactly and never showed it. Found as a SIGSEGV at startup in an Android armeabi-v7a build of DarkChan (_ht_new_node <- _ht_put_ptr <- n_gui_add_label), 2026-08-19.
Owning the buffer in one place is the point: the callers only ever wanted the first HASH_VALUE of the digest, and none of them can now get the size wrong. memcpy rather than a cast keeps it free of aliasing assumptions, and takes the same leading bytes the callers used before, so hash values are unchanged on 64-bit.
function _ht_get_string_trie
int _ht_get_string_trie(
HASH_TABLE * table,
const char * key,
char ** val
)
Retrieve an char *string value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val a pointer to a destination char *string
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_string
int _ht_get_string(
HASH_TABLE * table,
const char * key,
char ** val
)
Retrieve a char *string value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val A pointer to an empty destination char *string
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_ptr_trie
int _ht_get_ptr_trie(
HASH_TABLE * table,
const char * key,
void ** val
)
Retrieve a pointer value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val a pointer to a destination pointer
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_ptr
int _ht_get_ptr(
HASH_TABLE * table,
const char * key,
void ** val
)
Retrieve a pointer value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val A pointer to an empty pointer store
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_node_trie
HASH_NODE * _ht_get_node_trie(
HASH_TABLE * table,
const char * key
)
retrieve a HASH_NODE at key from table
Parameters:
- table targeted hash table
- key associated value's key
Return: a HASH_NODE *node or NULL
function _ht_get_node
HASH_NODE * _ht_get_node(
HASH_TABLE * table,
const char * key
)
return the associated key's node inside the hash_table
Parameters:
- table targeted table
- key Associated value's key
Return: The found node, or NULL
function _ht_get_int_trie
int _ht_get_int_trie(
HASH_TABLE * table,
const char * key,
int64_t * val
)
Retrieve an integral value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val a pointer to a destination integer
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_int
int _ht_get_int(
HASH_TABLE * table,
const char * key,
int64_t * val
)
Retrieve an integral value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val A pointer to a destination integer
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_double_trie
int _ht_get_double_trie(
HASH_TABLE * table,
const char * key,
double * val
)
Retrieve an double value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val a pointer to a destination double
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_get_double
int _ht_get_double(
HASH_TABLE * table,
const char * key,
double * val
)
Retrieve a double value in the hash table, at the given key.
Parameters:
- table targeted hash table
- key associated value's key
- val A pointer to a destination double
Return: TRUE or FALSE.
Leave val untouched if key is not found.
function _ht_find_longest_prefix_trie
char * _ht_find_longest_prefix_trie(
HASH_TABLE * table,
const char * key
)
find the longest prefix string that is not the current key
Parameters:
- table targeted hash table
- key key to search prefix for
Return: TRUE or FALSE
function _ht_depth_first_search
int _ht_depth_first_search(
HASH_NODE * node,
LIST * results
)
recursive, helper for ht_get_completion_list, get the list of leaf starting from node
Parameters:
- node starting node
- results initialized LIST * for the matching NODES
Return: TRUE or FALSE
function _ht_check_trie_divergence
size_t _ht_check_trie_divergence(
HASH_TABLE * table,
const char * key
)
check and return branching index in key if any
Parameters:
- table targeted hash table
- key associated value's key
Return: index of the divergence or SIZE_MAX on error
function _empty_ht_trie
int _empty_ht_trie(
HASH_TABLE * table
)
Empty a TRIE hash table.
Parameters:
- table targeted hash table
Return: TRUE or FALSE.
function _empty_ht
int _empty_ht(
HASH_TABLE * table
)
Empty a hash table (CLASSIC mode).
Parameters:
- table targeted hash table
Return: TRUE or FALSE.
function _destroy_ht_trie
int _destroy_ht_trie(
HASH_TABLE ** table
)
Free and set the table to NULL (TRIE mode).
Parameters:
- table targeted hash table
Return: TRUE or FALSE.
function _destroy_ht
int _destroy_ht(
HASH_TABLE ** table
)
Free and set the table to NULL.
Parameters:
- table targeted hash table
Return: TRUE or FALSE.
Attributes Documentation
variable _ht_table_counter
static uint64_t _ht_table_counter = 0;
variable _ht_process_key
static uint64_t _ht_process_key = 0;
variable _ht_key_once
static pthread_once_t _ht_key_once = PTHREAD_ONCE_INIT;
variable _ht_counter_lock
static pthread_mutex_t _ht_counter_lock = PTHREAD_MUTEX_INITIALIZER;
Macros Documentation
define ROTL64
#define ROTL64(
x,
r
)
(((x) << (r)) | ((x) >> (64 - (r))))
64 bit rotate left
define ROTL32
#define ROTL32(
x,
r
)
(((x) << (r)) | ((x) >> (32 - (r))))
32 bit rotate left
define BYTESWAP64
#define BYTESWAP64(
x
)
(((uint64_t)(x) << 56) | \
(((uint64_t)(x) << 40) & 0X00FF000000000000ULL) | \
(((uint64_t)(x) << 24) & 0X0000FF0000000000ULL) | \
(((uint64_t)(x) << 8) & 0X000000FF00000000ULL) | \
(((uint64_t)(x) >> 8) & 0X00000000FF000000ULL) | \
(((uint64_t)(x) >> 24) & 0X0000000000FF0000ULL) | \
(((uint64_t)(x) >> 40) & 0X000000000000FF00ULL) | \
((uint64_t)(x) >> 56))
32 bits bytes swap
define BYTESWAP32
#define BYTESWAP32(
x
)
((((x) & 0xFF) << 24) | (((x) >> 24) & 0xFF) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8))
32 bits bytes swap
define BIG_CONSTANT
#define BIG_CONSTANT(
x
)
(x##LLU)
max unsigned long long
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_hash.h"
#include "nilorea/n_str.h"
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
#include <sys/random.h>
#define N_HASH_HAVE_GETRANDOM 1
#endif
#ifdef __windows__
/* n_windows.h brings winsock2 (htonl) and must come first: bcrypt.h uses LONG/ULONG without defining them;
* link with -lbcrypt (in the Windows CLIBS) */
#include "nilorea/n_windows.h"
#include <bcrypt.h>
#else
#include <arpa/inet.h>
#endif
/* Trie tree tables */
HASH_NODE* _ht_new_node_trie(HASH_TABLE* table, const char key) {
__n_assert(table, return NULL);
HASH_NODE* new_hash_node = NULL;
Malloc(new_hash_node, HASH_NODE, 1);
__n_assert(new_hash_node, n_log(LOG_ERR, "Could not allocate new_hash_node"); return NULL);
new_hash_node->key = NULL;
new_hash_node->hash_value = 0;
new_hash_node->data.ptr = NULL;
new_hash_node->destroy_func = NULL;
new_hash_node->children = NULL;
new_hash_node->is_leaf = 0;
new_hash_node->need_rehash = 0;
new_hash_node->alphabet_length = table->alphabet_length;
Malloc(new_hash_node->children, HASH_NODE*, table->alphabet_length);
__n_assert(new_hash_node->children, n_log(LOG_ERR, "Could not allocate new_hash_node"); Free(new_hash_node); return NULL);
/* n_log( LOG_DEBUG , "node: %d %c table: alpha: %d / offset %d" , key , key , table -> alphabet_length , table -> alphabet_offset ); */
for (size_t it = 0; it < table->alphabet_length; it++) {
new_hash_node->children[it] = NULL;
}
new_hash_node->is_leaf = 0;
new_hash_node->key_id = key;
return new_hash_node;
} /* _ht_new_node_trie(...) */
int _ht_is_leaf_node_trie(HASH_TABLE* table, const char* key) {
__n_assert(table, return -1);
/* checks if the prefix match of key and root is a leaf node */
HASH_NODE* node = table->root;
for (size_t it = 0; key[it]; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index]) {
node = node->children[index];
} else {
return 0;
}
}
return node->is_leaf;
} /* _ht_is_leaf_node_trie(...) */
void _ht_node_destroy(void* node) {
HASH_NODE* node_ptr = (HASH_NODE*)node;
__n_assert(node_ptr, return);
if (node_ptr->type == HASH_STRING) {
Free(node_ptr->data.string);
}
if (node_ptr->type == HASH_PTR) {
if (node_ptr->destroy_func && node_ptr->data.ptr) {
node_ptr->destroy_func(node_ptr->data.ptr);
}
/* No free by default. must be passed as a destroy_func
else
{
Free( node_ptr -> data . ptr );
}
*/
}
FreeNoLog(node_ptr->key);
if (node_ptr->alphabet_length > 0) {
for (size_t it = 0; it < node_ptr->alphabet_length; it++) {
if (node_ptr->children[it]) {
_ht_node_destroy(node_ptr->children[it]);
}
}
Free(node_ptr->children);
}
Free(node_ptr);
} /* _ht_node_destroy */
size_t _ht_check_trie_divergence(HASH_TABLE* table, const char* key) {
__n_assert(table, return SIZE_MAX);
__n_assert(key, return SIZE_MAX);
__n_assert(table->root, return SIZE_MAX);
HASH_NODE* node = table->root;
size_t last_index = 0;
/* `node` is non-null on entry (table->root checked above) and only ever
* gets reassigned from `node->children[index]` after that slot was
* verified non-null. We still gate the loop on `node` so the static
* analyzer narrows `node` to non-null at every dereference inside the
* body, older clang static analyzers (the one in Ubuntu's clang-tools
* package, used by CI) don't track that constraint through the
* children[] reassignment, even though it holds. */
for (size_t it = 0; node && key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
HASH_NODE* child = node->children[index];
if (child) {
/* child check */
for (size_t it2 = 0; it2 < table->alphabet_length; it2++) {
if (it2 != (unsigned)index && node->children[it2]) {
/* child found, update branch index */
last_index = it + 1;
break;
}
}
/* Go to the next child in the sequence */
node = child;
}
}
return last_index;
} /* _ht_check_trie_divergence(...) */
char* _ht_find_longest_prefix_trie(HASH_TABLE* table, const char* key) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
size_t len = strlen(key);
/* start with full key and backtrack to search for divergences */
char* longest_prefix = NULL;
Malloc(longest_prefix, char, len + 1);
__n_assert(longest_prefix, return NULL);
memcpy(longest_prefix, key, len);
/* memcpy does not add a null terminator; set it explicitly */
longest_prefix[len] = '\0';
/* check branching from the root */
size_t branch_index = _ht_check_trie_divergence(table, longest_prefix);
if (branch_index > 0 && branch_index != SIZE_MAX) {
/* truncate to the branch point */
longest_prefix[branch_index - 1] = '\0';
/* shrink the allocation; Realloc preserves the original pointer on
failure so the caller still gets a valid terminated string */
Realloc(longest_prefix, char, branch_index + 1);
}
return longest_prefix;
} /* _ht_find_longest_prefix_trie(...) */
int _ht_remove_trie(HASH_TABLE* table, const char* key) {
__n_assert(table, return FALSE);
__n_assert(table->root, return FALSE);
__n_assert(key, return FALSE);
/* stop if matching node not a leaf node */
if (!_ht_is_leaf_node_trie(table, key)) {
return FALSE;
}
HASH_NODE* node = table->root;
/* find the longest prefix string that is not the current key */
char* longest_prefix = _ht_find_longest_prefix_trie(table, key);
if (!longest_prefix) {
n_log(LOG_ERR, "couldn't find longest prefix for key %s", key);
return FALSE;
}
if (longest_prefix[0] == '\0') {
Free(longest_prefix);
return FALSE;
}
/* keep track of position in the tree */
size_t it = 0;
for (it = 0; longest_prefix[it] != '\0'; it++) {
size_t index = (size_t)longest_prefix[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index] != NULL) {
/* common prefix, keep moving */
node = node->children[index];
} else {
/* not found */
Free(longest_prefix);
return FALSE;
}
}
/* deepest common node between the two strings */
/* deleting the sequence corresponding to key */
for (; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index]) {
/* delete the remaining sequence */
HASH_NODE* node_to_kill = node->children[index];
node->children[index] = NULL;
_ht_node_destroy(node_to_kill);
}
}
Free(longest_prefix);
table->nb_keys--;
return TRUE;
} /* _ht_remove_trie(...) */
int _ht_put_int_trie(HASH_TABLE* table, const char* key, HASH_INT_TYPE value) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
HASH_NODE* node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
/* n_log( LOG_DEBUG , "index:%d" , index ); */
if (node->children[index] == NULL) {
/* create a node */
node->children[index] = _ht_new_node_trie(table, key[it]);
__n_assert(node->children[index], return FALSE);
} else {
/* nothing to do since node is existing */
}
/* go down a level, to the child referenced by index */
node = node->children[index];
}
/* At the end of the key, mark this node as the leaf node */
int was_leaf = node->is_leaf;
if (was_leaf) {
FreeNoLog(node->key);
}
node->is_leaf = 1;
/* Put the key */
node->key = strdup(key);
if (!node->key) {
n_log(LOG_ERR, "strdup failure for key [%s]", key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
/* Put the value */
node->data.ival = value;
node->type = HASH_INT;
if (!was_leaf) {
table->nb_keys++;
}
return TRUE;
} /* _ht_put_int_trie(...) */
int _ht_put_double_trie(HASH_TABLE* table, const char* key, double value) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
HASH_NODE* node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index] == NULL) {
/* create a node */
node->children[index] = _ht_new_node_trie(table, key[it]);
__n_assert(node->children[index], return FALSE);
} else {
/* nothing to do since node is existing */
}
/* go down a level, to the child referenced by index */
node = node->children[index];
}
/* At the end of the key, mark this node as the leaf node */
int was_leaf = node->is_leaf;
if (was_leaf) {
FreeNoLog(node->key);
}
node->is_leaf = 1;
/* Put the key */
node->key = strdup(key);
if (!node->key) {
n_log(LOG_ERR, "strdup failure for key [%s]", key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
/* Put the value */
node->data.fval = value;
node->type = HASH_DOUBLE;
if (!was_leaf) {
table->nb_keys++;
}
return TRUE;
} /* _ht_put_double_trie(...) */
// cppcheck-suppress constParameterCallback ; callback signature must match typedef
int _ht_put_string_trie(HASH_TABLE* table, const char* key, char* string) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
HASH_NODE* node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index] == NULL) {
/* create a node */
node->children[index] = _ht_new_node_trie(table, key[it]);
__n_assert(node->children[index], return FALSE);
} else {
/* nothing to do since node is existing */
}
/* go down a level, to the child referenced by index */
node = node->children[index];
}
/* At the end of the key, mark this node as the leaf node */
int was_leaf = node->is_leaf;
if (was_leaf) {
FreeNoLog(node->key);
FreeNoLog(node->data.string);
}
node->is_leaf = 1;
/* Put the key */
node->key = strdup(key);
if (!node->key) {
n_log(LOG_ERR, "strdup failure for key [%s]", key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
/* Put the value */
if (string) {
node->data.string = strdup(string);
if (!node->data.string) {
n_log(LOG_ERR, "strdup failure for value at key [%s]", key);
Free(node->key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
} else {
node->data.string = NULL;
}
node->type = HASH_STRING;
if (!was_leaf) {
table->nb_keys++;
}
return TRUE;
} /* _ht_put_string_trie(...) */
int _ht_put_string_ptr_trie(HASH_TABLE* table, const char* key, char* string) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
HASH_NODE* node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for charater at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index] == NULL) {
/* create a node */
node->children[index] = _ht_new_node_trie(table, key[it]);
__n_assert(node->children[index], return FALSE);
} else {
/* nothing to do since node is existing */
}
/* go down a level, to the child referenced by index */
node = node->children[index];
}
/* At the end of the key, mark this node as the leaf node */
int was_leaf = node->is_leaf;
if (was_leaf) {
FreeNoLog(node->key);
FreeNoLog(node->data.string);
}
node->is_leaf = 1;
/* Put the key - string pointer variant does not strdup the value, but key must be duplicated */
node->key = strdup(key);
if (!node->key) {
n_log(LOG_ERR, "strdup failure for key [%s]", key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
/* Put the string pointer (not a copy - caller owns the string) */
node->data.string = string;
node->type = HASH_STRING;
if (!was_leaf) {
table->nb_keys++;
}
return TRUE;
} /* _ht_put_string_ptr_trie(...) */
int _ht_put_ptr_trie(HASH_TABLE* table, const char* key, void* ptr, void (*destructor)(void* ptr), void* (*duplicator)(void* ptr)) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
HASH_NODE* node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_ERR, "Invalid value %zu for character at position %zu of %s, set to 0", index, it, key);
index = 0;
}
if (node->children[index] == NULL) {
/* create a node */
node->children[index] = _ht_new_node_trie(table, key[it]);
__n_assert(node->children[index], return FALSE);
} else {
/* nothing to do since node is existing */
}
/* go down a level, to the child referenced by index */
node = node->children[index];
}
/* At the end of the key, mark this node as the leaf node */
int was_leaf = node->is_leaf;
if (was_leaf) {
FreeNoLog(node->key);
/* free old value if a destructor was set */
if (node->destroy_func && node->data.ptr) {
node->destroy_func(node->data.ptr);
}
}
node->is_leaf = 1;
/* Put the key */
node->key = strdup(key);
if (!node->key) {
n_log(LOG_ERR, "strdup failure for key [%s]", key);
table->nb_keys++; /* compensate for upcoming remove */
_ht_remove_trie(table, key);
return FALSE;
}
/* Put the value */
node->data.ptr = ptr;
node->destroy_func = destructor;
node->duplicate_func = duplicator;
node->type = HASH_PTR;
if (!was_leaf) {
table->nb_keys++;
}
return TRUE;
} /* _ht_put_ptr_trie(...) */
HASH_NODE* _ht_get_node_trie(HASH_TABLE* table, const char* key) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* node = NULL;
if (key[0] != '\0') {
node = table->root;
for (size_t it = 0; key[it] != '\0'; it++) {
size_t index = (size_t)key[it] - table->alphabet_offset;
if (index >= table->alphabet_length) {
n_log(LOG_DEBUG, "Invalid value %zu for charater at index %zu of %s, set to 0", index, it, key);
return NULL;
}
if (node->children[index] == NULL) {
/* not found */
return NULL;
}
node = node->children[index];
}
} else {
node = NULL;
}
return node;
} /* _ht_get_node_trie(...) */
int _ht_get_int_trie(HASH_TABLE* table, const char* key, HASH_INT_TYPE* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node_trie(table, key);
if (!node || !node->is_leaf)
return FALSE;
if (node->type != HASH_INT) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_INT, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.ival;
return TRUE;
} /* _ht_get_int_trie() */
int _ht_get_double_trie(HASH_TABLE* table, const char* key, double* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node_trie(table, key);
if (!node || !node->is_leaf)
return FALSE;
if (node->type != HASH_DOUBLE) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_DOUBLE, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.fval;
return TRUE;
} /* _ht_get_double_trie() */
int _ht_get_string_trie(HASH_TABLE* table, const char* key, char** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node_trie(table, key);
if (!node || !node->is_leaf)
return FALSE;
if (node->type != HASH_STRING) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_STRING, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.string;
return TRUE;
} /* _ht_get_string_trie() */
int _ht_get_ptr_trie(HASH_TABLE* table, const char* key, void** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node_trie(table, key);
if (!node || !node->is_leaf)
return FALSE;
if (node->type != HASH_PTR) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_PTR , key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.ptr;
return TRUE;
} /* _ht_get_ptr_trie() */
int _empty_ht_trie(HASH_TABLE* table) {
__n_assert(table, return FALSE);
_ht_node_destroy(table->root);
table->root = _ht_new_node_trie(table, '\0');
table->nb_keys = 0;
return TRUE;
} /* _empty_ht_trie */
int _destroy_ht_trie(HASH_TABLE** table) {
__n_assert(table && (*table), n_log(LOG_ERR, "Can't destroy table: already NULL"); return FALSE);
_ht_node_destroy((*table)->root);
Free((*table));
return TRUE;
} /* _destroy_ht_trie */
void _ht_print_trie_helper(HASH_TABLE* table, HASH_NODE* node) {
if (!node)
return;
if (node->is_leaf) {
printf("key: %s, val: ", node->key);
switch (node->type) {
case HASH_INT:
printf("int: %ld", (long)node->data.ival);
break;
case HASH_DOUBLE:
printf("double: %f", node->data.fval);
break;
case HASH_PTR:
printf("ptr: %p", node->data.ptr);
break;
case HASH_STRING:
printf("%s", node->data.string);
break;
default:
printf("unknwow type %d", node->type);
break;
}
printf("\n");
}
for (size_t it = 0; it < table->alphabet_length; it++) {
_ht_print_trie_helper(table, node->children[it]);
}
} /* _ht_print_trie_helper(...) */
void _ht_print_trie(HASH_TABLE* table) {
__n_assert(table, return);
__n_assert(table->root, return);
HASH_NODE* node = table->root;
_ht_print_trie_helper(table, node);
return;
} /* _ht_print_trie(...) */
void _ht_search_trie_helper(LIST* results, HASH_NODE* node, int (*node_is_matching)(HASH_NODE* node)) {
if (!node)
return;
if (node->is_leaf) {
if (node_is_matching(node) == TRUE) {
list_push(results, strdup(node->key), &free);
}
}
for (size_t it = 0; it < node->alphabet_length; it++) {
_ht_search_trie_helper(results, node->children[it], node_is_matching);
}
}
LIST* _ht_search_trie(HASH_TABLE* table, int (*node_is_matching)(HASH_NODE* node)) {
__n_assert(table, return NULL);
LIST* results = new_generic_list(MAX_LIST_ITEMS);
__n_assert(results, return NULL);
_ht_search_trie_helper(results, table->root, node_is_matching);
if (results->nb_items < 1)
list_destroy(&results);
return results;
} /* _ht_search_trie(...) */
int _ht_depth_first_search(HASH_NODE* node, LIST* results) {
__n_assert(results, return FALSE);
if (!node)
return FALSE;
for (size_t it = 0; it < node->alphabet_length; it++) {
_ht_depth_first_search(node->children[it], results);
}
if (node->is_leaf) {
if (results->nb_items < results->nb_max_items) {
return list_push(results, strdup(node->key), &free);
}
return TRUE;
}
return TRUE;
} /* _ht_depth_first_search(...) */
/* Classic hash table */
#define ROTL64(x, r) (((x) << (r)) | ((x) >> (64 - (r))))
#define ROTL32(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
#define BIG_CONSTANT(x) (x##LLU)
/* NO-OP for little-endian platforms */
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define BYTESWAP32(x) (x)
#define BYTESWAP64(x) (x)
#endif
/* if __BYTE_ORDER__ is not predefined (like FreeBSD), use arch */
#elif defined(__i386) || defined(__x86_64) || defined(__alpha) || defined(__vax)
#define BYTESWAP32(x) (x)
#define BYTESWAP64(x) (x)
/* use __builtin_bswap32 if available */
#elif defined(__GNUC__) || defined(__clang__)
#ifdef __has_builtin
#if __has_builtin(__builtin_bswap32)
#define BYTESWAP32(x) __builtin_bswap32(x)
#endif // __has_builtin(__builtin_bswap32)
#if __has_builtin(__builtin_bswap64)
#define BYTESWAP64(x) __builtin_bswap64(x)
#endif // __has_builtin(__builtin_bswap64)
#endif // __has_builtin
#endif // defined(__GNUC__) || defined(__clang__)
/* last resort (big-endian w/o __builtin_bswap) */
#ifndef BYTESWAP32
#define BYTESWAP32(x) ((((x) & 0xFF) << 24) | (((x) >> 24) & 0xFF) | (((x) & 0x0000FF00) << 8) | (((x) & 0x00FF0000) >> 8))
#endif
#ifndef BYTESWAP64
#define BYTESWAP64(x) \
(((uint64_t)(x) << 56) | \
(((uint64_t)(x) << 40) & 0X00FF000000000000ULL) | \
(((uint64_t)(x) << 24) & 0X0000FF0000000000ULL) | \
(((uint64_t)(x) << 8) & 0X000000FF00000000ULL) | \
(((uint64_t)(x) >> 8) & 0X00000000FF000000ULL) | \
(((uint64_t)(x) >> 24) & 0X0000000000FF0000ULL) | \
(((uint64_t)(x) >> 40) & 0X000000000000FF00ULL) | \
((uint64_t)(x) >> 56))
#endif
FORCE_INLINE uint32_t getblock32(const uint32_t* p, const size_t i) {
uint32_t result;
memcpy(&result, (const uint8_t*)p + i * 4, sizeof(result));
return BYTESWAP32(result);
}
FORCE_INLINE uint64_t getblock64(const uint64_t* p, const size_t i) {
uint64_t result;
memcpy(&result, (const uint8_t*)p + i * 8, sizeof(result));
return BYTESWAP64(result);
}
FORCE_INLINE uint32_t fmix32(uint32_t h) {
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
} /* fmix32(...) */
FORCE_INLINE uint64_t fmix64(uint64_t k) {
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
// Safe forward-indexing version
void MurmurHash3_x86_32(const void* key, const size_t len, const uint32_t seed, void* out) {
const uint8_t* data = (const uint8_t*)key;
const size_t nblocks = len / 4;
uint32_t h1 = seed;
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
const uint32_t* blocks = (const uint32_t*)data;
for (size_t i = 0; i < nblocks; i++) {
uint32_t k1 = getblock32(blocks, i);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
const uint8_t* tail = data + nblocks * 4;
uint32_t k1 = 0;
switch (len & 3) {
case 3:
k1 ^= (uint32_t)tail[2] << 16; /* fall through */
FALL_THROUGH;
case 2:
k1 ^= (uint32_t)tail[1] << 8; /* fall through */
FALL_THROUGH;
case 1:
k1 ^= (uint32_t)tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
break;
default:
break;
}
h1 ^= (uint32_t)len;
h1 = fmix32(h1);
*(uint32_t*)out = h1;
} /* MurmurHash3_x86_32 */
void MurmurHash3_x86_128(const void* key, const size_t len, const uint32_t seed, void* out) {
const uint8_t* data = (const uint8_t*)key;
const size_t nblocks = len / 16;
uint32_t h1 = seed;
uint32_t h2 = seed;
uint32_t h3 = seed;
uint32_t h4 = seed;
const uint32_t c1 = 0x239b961b;
const uint32_t c2 = 0xab0e9789;
const uint32_t c3 = 0x38b34ae5;
const uint32_t c4 = 0xa1e38b93;
const uint32_t* blocks = (const uint32_t*)data;
for (size_t i = 0; i < nblocks; i++) {
uint32_t k1 = getblock32(blocks, i * 4 + 0);
uint32_t k2 = getblock32(blocks, i * 4 + 1);
uint32_t k3 = getblock32(blocks, i * 4 + 2);
uint32_t k4 = getblock32(blocks, i * 4 + 3);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 19);
h1 += h2;
h1 = h1 * 5 + 0x561ccd1b;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
h2 = ROTL32(h2, 17);
h2 += h3;
h2 = h2 * 5 + 0x0bcaa747;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
h3 = ROTL32(h3, 15);
h3 += h4;
h3 = h3 * 5 + 0x96cd1c35;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
h4 = ROTL32(h4, 13);
h4 += h1;
h4 = h4 * 5 + 0x32ac3b17;
}
// tail
const uint8_t* tail = data + nblocks * 16;
uint32_t k1 = 0, k2 = 0, k3 = 0, k4 = 0;
switch (len & 15) {
case 15:
k4 ^= (uint32_t)tail[14] << 16; /* fall through */
FALL_THROUGH;
case 14:
k4 ^= (uint32_t)tail[13] << 8; /* fall through */
FALL_THROUGH;
case 13:
k4 ^= (uint32_t)tail[12];
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
/* fall through */
FALL_THROUGH;
case 12:
k3 ^= (uint32_t)tail[11] << 24; /* fall through */
FALL_THROUGH;
case 11:
k3 ^= (uint32_t)tail[10] << 16; /* fall through */
FALL_THROUGH;
case 10:
k3 ^= (uint32_t)tail[9] << 8; /* fall through */
FALL_THROUGH;
case 9:
k3 ^= (uint32_t)tail[8];
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
/* fall through */
FALL_THROUGH;
case 8:
k2 ^= (uint32_t)tail[7] << 24; /* fall through */
FALL_THROUGH;
case 7:
k2 ^= (uint32_t)tail[6] << 16; /* fall through */
FALL_THROUGH;
case 6:
k2 ^= (uint32_t)tail[5] << 8; /* fall through */
FALL_THROUGH;
case 5:
k2 ^= (uint32_t)tail[4];
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
/* fall through */
FALL_THROUGH;
case 4:
k1 ^= (uint32_t)tail[3] << 24; /* fall through */
FALL_THROUGH;
case 3:
k1 ^= (uint32_t)tail[2] << 16; /* fall through */
FALL_THROUGH;
case 2:
k1 ^= (uint32_t)tail[1] << 8; /* fall through */
FALL_THROUGH;
case 1:
k1 ^= (uint32_t)tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
break;
default:
break;
}
// finalization
h1 ^= (uint32_t)len;
h2 ^= (uint32_t)len;
h3 ^= (uint32_t)len;
h4 ^= (uint32_t)len;
h1 += h2 + h3 + h4;
h2 += h1;
h3 += h1;
h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2 + h3 + h4;
h2 += h1;
h3 += h1;
h4 += h1;
((uint32_t*)out)[0] = h1;
((uint32_t*)out)[1] = h2;
((uint32_t*)out)[2] = h3;
((uint32_t*)out)[3] = h4;
} /* MurmurHash3_x86_128(...) */
void MurmurHash3_x64_128(const void* key, const size_t len, const uint64_t seed, void* out) {
const uint8_t* data = (const uint8_t*)key;
const size_t nblocks = len / 16;
uint64_t h1 = seed;
uint64_t h2 = seed;
const uint64_t c1 = 0x87c37b91114253d5ULL;
const uint64_t c2 = 0x4cf5ad432745937fULL;
// Body
const uint64_t* blocks = (const uint64_t*)data;
for (size_t i = 0; i < nblocks; i++) {
uint64_t k1 = getblock64(blocks, i * 2 + 0);
uint64_t k2 = getblock64(blocks, i * 2 + 1);
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
h1 = ROTL64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
h2 = ROTL64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}
// Tail
const uint8_t* tail = data + nblocks * 16;
uint64_t k1 = 0;
uint64_t k2 = 0;
switch (len & 15) {
case 15:
k2 ^= ((uint64_t)tail[14]) << 48; /* fall through */
FALL_THROUGH;
case 14:
k2 ^= ((uint64_t)tail[13]) << 40; /* fall through */
FALL_THROUGH;
case 13:
k2 ^= ((uint64_t)tail[12]) << 32; /* fall through */
FALL_THROUGH;
case 12:
k2 ^= ((uint64_t)tail[11]) << 24; /* fall through */
FALL_THROUGH;
case 11:
k2 ^= ((uint64_t)tail[10]) << 16; /* fall through */
FALL_THROUGH;
case 10:
k2 ^= ((uint64_t)tail[9]) << 8; /* fall through */
FALL_THROUGH;
case 9:
k2 ^= ((uint64_t)tail[8]);
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
/* fall through */
FALL_THROUGH;
case 8:
k1 ^= ((uint64_t)tail[7]) << 56; /* fall through */
FALL_THROUGH;
case 7:
k1 ^= ((uint64_t)tail[6]) << 48; /* fall through */
FALL_THROUGH;
case 6:
k1 ^= ((uint64_t)tail[5]) << 40; /* fall through */
FALL_THROUGH;
case 5:
k1 ^= ((uint64_t)tail[4]) << 32; /* fall through */
FALL_THROUGH;
case 4:
k1 ^= ((uint64_t)tail[3]) << 24; /* fall through */
FALL_THROUGH;
case 3:
k1 ^= ((uint64_t)tail[2]) << 16; /* fall through */
FALL_THROUGH;
case 2:
k1 ^= ((uint64_t)tail[1]) << 8; /* fall through */
FALL_THROUGH;
case 1:
k1 ^= ((uint64_t)tail[0]);
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
break;
default:
break;
}
// Finalization
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
((uint64_t*)out)[0] = h1;
((uint64_t*)out)[1] = h2;
} /* MurmurHash3_x64_128()*/
char* ht_node_type(const HASH_NODE* node) {
__n_assert(node, return "NULL_NODE");
switch (node->type) {
case HASH_INT:
return "HASH_INT";
case HASH_DOUBLE:
return "HASH_DOUBLE";
case HASH_STRING:
return "HASH_STRING";
case HASH_PTR:
return "HASH_PTR";
default:
return "HASH_UNKNOWN";
}
} /* ht_node_type(...) */
static HASH_VALUE _ht_hash_value(const HASH_TABLE* table, const char* key, size_t key_len) {
uint8_t digest[16] = {0};
HASH_VALUE value = 0;
MurmurHash(key, key_len, table->seed, digest);
memcpy(&value, digest, sizeof(value));
return value;
} /* _ht_hash_value */
HASH_NODE* _ht_get_node(HASH_TABLE* table, const char* key) {
HASH_VALUE hash_value = 0;
size_t index = 0;
__n_assert(table, return NULL);
__n_assert(key, return NULL);
if (key[0] == '\0')
return NULL;
hash_value = _ht_hash_value(table, key, strlen(key));
index = (hash_value) % (table->size);
if (!table->hash_table[index]->start)
return NULL;
list_foreach(list_node, table->hash_table[index]) {
HASH_NODE* node_ptr = (HASH_NODE*)list_node->ptr;
if (!strcmp(key, node_ptr->key)) {
return node_ptr;
}
}
return NULL;
} /* _ht_get_node() */
HASH_NODE* _ht_new_node(const HASH_TABLE* table, const char* key) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
HASH_VALUE hash_value = 0;
if (strlen(key) == 0)
return NULL;
hash_value = _ht_hash_value(table, key, strlen(key));
Malloc(new_hash_node, HASH_NODE, 1);
__n_assert(new_hash_node, n_log(LOG_ERR, "Could not allocate new_hash_node"); return NULL);
new_hash_node->key = strdup(key);
new_hash_node->key_id = '\0';
__n_assert(new_hash_node->key, n_log(LOG_ERR, "Could not allocate new_hash_node->key"); Free(new_hash_node); return NULL);
new_hash_node->hash_value = hash_value;
new_hash_node->data.ptr = NULL;
new_hash_node->destroy_func = NULL;
new_hash_node->children = NULL;
new_hash_node->is_leaf = 0;
new_hash_node->need_rehash = 0;
new_hash_node->alphabet_length = 0;
return new_hash_node;
} /* _ht_new_node */
HASH_NODE* _ht_new_int_node(HASH_TABLE* table, const char* key, HASH_INT_TYPE value) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
new_hash_node = _ht_new_node(table, key);
if (new_hash_node) {
new_hash_node->data.ival = value;
new_hash_node->type = HASH_INT;
} else {
n_log(LOG_ERR, "Could not get a new node in table %p with key %s", table, key);
}
return new_hash_node;
} /* _ht_new_int_node */
HASH_NODE* _ht_new_double_node(HASH_TABLE* table, const char* key, double value) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
new_hash_node = _ht_new_node(table, key);
if (new_hash_node) {
new_hash_node->data.fval = value;
new_hash_node->type = HASH_DOUBLE;
} else {
n_log(LOG_ERR, "Could not get a new node in table %p with key %s", table, key);
}
return new_hash_node;
} /* _ht_new_double_node */
HASH_NODE* _ht_new_string_node(HASH_TABLE* table, const char* key, const char* value) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
new_hash_node = _ht_new_node(table, key);
if (new_hash_node) {
if (value)
new_hash_node->data.string = strdup(value);
else
new_hash_node->data.string = NULL;
new_hash_node->type = HASH_STRING;
} else {
n_log(LOG_ERR, "Could not get a new node in table %p with key %s", table, key);
}
return new_hash_node;
}
HASH_NODE* _ht_new_string_ptr_node(HASH_TABLE* table, const char* key, char* value) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
new_hash_node = _ht_new_node(table, key);
if (new_hash_node) {
new_hash_node->data.string = value;
new_hash_node->type = HASH_STRING;
} else {
n_log(LOG_ERR, "Could not get a new node in table %p with key %s", table, key);
}
return new_hash_node;
}
HASH_NODE* _ht_new_ptr_node(HASH_TABLE* table, const char* key, void* value, void (*destructor)(void* ptr), void* (*duplicator)(void* ptr)) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
HASH_NODE* new_hash_node = NULL;
new_hash_node = _ht_new_node(table, key);
if (new_hash_node) {
new_hash_node->data.ptr = value;
new_hash_node->destroy_func = destructor;
new_hash_node->duplicate_func = duplicator;
new_hash_node->type = HASH_PTR;
} else {
n_log(LOG_ERR, "Could not get a new node in table %p with key %s", table, key);
}
return new_hash_node;
}
int _ht_put_int(HASH_TABLE* table, const char* key, HASH_INT_TYPE value) {
HASH_NODE* node_ptr = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if ((node_ptr = _ht_get_node(table, key))) {
if (node_ptr->type == HASH_INT) {
node_ptr->data.ival = value;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_INT, key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
int retcode = FALSE;
node_ptr = _ht_new_int_node(table, key, value);
if (node_ptr) {
size_t index = (node_ptr->hash_value) % (table->size);
retcode = list_push(table->hash_table[index], node_ptr, &_ht_node_destroy);
if (retcode == TRUE) {
table->nb_keys++;
}
}
return retcode;
} /*_ht_put_int() */
int _ht_put_double(HASH_TABLE* table, const char* key, double value) {
HASH_NODE* node_ptr = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if ((node_ptr = _ht_get_node(table, key))) {
if (node_ptr->type == HASH_DOUBLE) {
node_ptr->data.fval = value;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_DOUBLE, key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
int retcode = FALSE;
node_ptr = _ht_new_double_node(table, key, value);
if (node_ptr) {
HASH_VALUE index = (node_ptr->hash_value) % (table->size);
retcode = list_push(table->hash_table[index], node_ptr, &_ht_node_destroy);
if (retcode == TRUE) {
table->nb_keys++;
}
}
return retcode;
} /*_ht_put_double()*/
int _ht_put_ptr(HASH_TABLE* table, const char* key, void* ptr, void (*destructor)(void* ptr), void* (*duplicator)(void* ptr)) {
HASH_NODE* node_ptr = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if ((node_ptr = _ht_get_node(table, key))) {
/* let's check the key isn't already assigned with another data type */
if (node_ptr->type == HASH_PTR) {
/* free the old value if a destructor is set */
if (node_ptr->destroy_func && node_ptr->data.ptr) {
node_ptr->destroy_func(node_ptr->data.ptr);
}
/* always update the pointer and function pointers */
node_ptr->data.ptr = ptr;
node_ptr->destroy_func = destructor;
node_ptr->duplicate_func = duplicator;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_PTR , key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
int retcode = FALSE;
node_ptr = _ht_new_ptr_node(table, key, ptr, destructor, duplicator);
if (node_ptr) {
HASH_VALUE index = (node_ptr->hash_value) % (table->size);
retcode = list_push(table->hash_table[index], node_ptr, &_ht_node_destroy);
if (retcode == TRUE) {
table->nb_keys++;
}
}
return retcode;
} /* _ht_put_ptr() */
int _ht_put_string(HASH_TABLE* table, const char* key, char* string) {
HASH_NODE* node_ptr = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if ((node_ptr = _ht_get_node(table, key))) {
/* let's check the key isn't already assigned with another data type */
if (node_ptr->type == HASH_STRING) {
char* new_str = NULL;
if (string) {
new_str = strdup(string);
if (!new_str) {
n_log(LOG_ERR, "could not strdup char *string at %p, didn't overwrite %s", string, key);
return FALSE;
}
}
Free(node_ptr->data.string);
node_ptr->data.string = new_str;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_STRING , key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
int retcode = FALSE;
node_ptr = _ht_new_string_node(table, key, string);
if (node_ptr) {
HASH_VALUE index = (node_ptr->hash_value) % (table->size);
retcode = list_push(table->hash_table[index], node_ptr, &_ht_node_destroy);
if (retcode == TRUE) {
table->nb_keys++;
}
}
return retcode;
} /*_ht_put_string */
int _ht_put_string_ptr(HASH_TABLE* table, const char* key, char* string) {
HASH_NODE* node_ptr = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if ((node_ptr = _ht_get_node(table, key))) {
/* let's check the key isn't already assigned with another data type */
if (node_ptr->type == HASH_STRING) {
Free(node_ptr->data.string);
node_ptr->data.string = string;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_STRING , key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
int retcode = FALSE;
node_ptr = _ht_new_string_ptr_node(table, key, string);
if (node_ptr) {
HASH_VALUE index = (node_ptr->hash_value) % (table->size);
retcode = list_push(table->hash_table[index], node_ptr, &_ht_node_destroy);
if (retcode == TRUE) {
table->nb_keys++;
}
}
return retcode;
} /*_ht_put_string_ptr */
int _ht_get_int(HASH_TABLE* table, const char* key, HASH_INT_TYPE* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node(table, key);
if (!node)
return FALSE;
if (node->type != HASH_INT) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_INT, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.ival;
return TRUE;
} /* _ht_get_int() */
int _ht_get_double(HASH_TABLE* table, const char* key, double* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node(table, key);
if (!node)
return FALSE;
if (node->type != HASH_DOUBLE) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_DOUBLE, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.fval;
return TRUE;
} /* _ht_get_double()*/
int _ht_get_ptr(HASH_TABLE* table, const char* key, void** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node(table, key);
if (!node)
return FALSE;
if (node->type != HASH_PTR) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_PTR, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.ptr;
return TRUE;
} /* _ht_get_ptr() */
int _ht_get_string(HASH_TABLE* table, const char* key, char** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
HASH_NODE* node = _ht_get_node(table, key);
if (!node)
return FALSE;
if (node->type != HASH_STRING) {
n_log(LOG_ERR, "Can't get key[\"%s\"] of type HASH_STRING, key is type %s", key, ht_node_type(node));
return FALSE;
}
(*val) = node->data.string;
return TRUE;
} /* _ht_get_string() */
int _ht_remove(HASH_TABLE* table, const char* key) {
HASH_VALUE hash_value = 0;
size_t index = 0;
HASH_NODE* node_ptr = NULL;
LIST_NODE* node_to_kill = NULL;
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
if (strlen(key) == 0)
return FALSE;
hash_value = _ht_hash_value(table, key, strlen(key));
index = (hash_value) % (table->size);
if (!table->hash_table[index]->start) {
n_log(LOG_ERR, "Can't remove key[\"%s\"], table is empty", key);
return FALSE;
}
list_foreach(list_node, table->hash_table[index]) {
node_ptr = (HASH_NODE*)list_node->ptr;
/* if we found the same */
if (!strcmp(key, node_ptr->key)) {
node_to_kill = list_node;
break;
}
}
if (node_to_kill) {
node_ptr = remove_list_node(table->hash_table[index], node_to_kill, HASH_NODE);
_ht_node_destroy(node_ptr);
table->nb_keys--;
return TRUE;
}
n_log(LOG_ERR, "Can't delete key[\"%s\"]: inexisting key", key);
return FALSE;
} /* ht_remove() */
int _empty_ht(HASH_TABLE* table) {
__n_assert(table, return FALSE);
HASH_VALUE index = 0;
for (index = 0; index < table->size; index++) {
while (table->hash_table[index] && table->hash_table[index]->start) {
HASH_NODE* hash_node = remove_list_node(table->hash_table[index], table->hash_table[index]->start, HASH_NODE);
_ht_node_destroy(hash_node);
}
}
table->nb_keys = 0;
return TRUE;
} /* empty_ht */
int _destroy_ht(HASH_TABLE** table) {
__n_assert(table && (*table), n_log(LOG_ERR, "Can't destroy table: already NULL"); return FALSE);
if ((*table)->hash_table) {
// empty_ht( (*table) );
HASH_VALUE it = 0;
for (it = 0; it < (*table)->size; it++) {
if ((*table)->hash_table[it])
list_destroy(&(*table)->hash_table[it]);
}
Free((*table)->hash_table);
}
Free((*table));
return TRUE;
} /* _destroy_ht */
void _ht_print(HASH_TABLE* table) {
__n_assert(table, return);
__n_assert(table->hash_table, return);
// cppcheck-suppress constVariablePointer ; node is generated by ht_foreach macro
ht_foreach(node, table) {
const HASH_NODE* ht_node = (const HASH_NODE*)node->ptr;
printf("key:%s node:%s\n", ht_node->key, ht_node->key);
}
return;
} /* _ht_print(...) */
LIST* _ht_search(HASH_TABLE* table, int (*node_is_matching)(HASH_NODE* node)) {
__n_assert(table, return NULL);
LIST* results = new_generic_list(MAX_LIST_ITEMS);
__n_assert(results, return NULL);
ht_foreach(node, table) {
HASH_NODE* hnode = (HASH_NODE*)node->ptr;
if (node_is_matching(hnode) == TRUE) {
list_push(results, strdup(hnode->key), &free);
}
}
if (results->nb_items < 1)
list_destroy(&results);
return results;
} /* _ht_search(...) */
/* Hash tables function pointers and common table type functions */
HASH_TABLE* new_ht_trie(size_t alphabet_length, size_t alphabet_offset) {
HASH_TABLE* table = NULL;
Malloc(table, HASH_TABLE, 1);
__n_assert(table, n_log(LOG_ERR, "Error allocating HASH_TABLE *table"); return NULL);
table->size = 0;
table->seed = 0;
table->nb_keys = 0;
errno = 0;
table->hash_table = NULL;
table->ht_put_int = _ht_put_int_trie;
table->ht_put_double = _ht_put_double_trie;
table->ht_put_ptr = _ht_put_ptr_trie;
table->ht_put_string = _ht_put_string_trie;
table->ht_put_string_ptr = _ht_put_string_ptr_trie;
table->ht_get_int = _ht_get_int_trie;
table->ht_get_double = _ht_get_double_trie;
table->ht_get_string = _ht_get_string_trie;
table->ht_get_ptr = _ht_get_ptr_trie;
table->ht_remove = _ht_remove_trie;
table->ht_search = _ht_search_trie;
table->empty_ht = _empty_ht_trie;
table->destroy_ht = _destroy_ht_trie;
table->ht_print = _ht_print_trie;
table->alphabet_length = alphabet_length;
table->alphabet_offset = alphabet_offset;
table->root = _ht_new_node_trie(table, '\0');
if (!table->root) {
n_log(LOG_ERR, "Couldn't allocate new_ht_trie with alphabet_length of %zu and alphabet offset of %zu", alphabet_length, alphabet_offset);
Free(table);
return NULL;
}
table->mode = HASH_TRIE;
return table;
} /* new_ht_trie */
/* Per-process random key for the table seeds, read once from the OS: a
* hash table fed attacker-chosen keys (form fields, headers, cookies) must
* not use a seed the attacker can predict, or colliding keys can be
* precomputed to turn every lookup into a list walk (hash flooding). The
* old seed was rand() % 100000 with rand() never seeded: the same for
* every process. */
static uint64_t _ht_process_key = 0;
static uint64_t _ht_table_counter = 0;
static pthread_once_t _ht_key_once = PTHREAD_ONCE_INIT;
static pthread_mutex_t _ht_counter_lock = PTHREAD_MUTEX_INITIALIZER;
static void _ht_key_init(void) {
uint64_t k = 0;
int got = 0;
#if defined(__windows__)
got = BCryptGenRandom(NULL, (PUCHAR)&k, (ULONG)sizeof(k), BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0;
#else
#ifdef N_HASH_HAVE_GETRANDOM
got = getrandom(&k, sizeof(k), GRND_NONBLOCK) == (ssize_t)sizeof(k);
#endif
/* cppcheck does not define __GLIBC__, so it never sees the getrandom()
* branch above that can set got */
// cppcheck-suppress knownConditionTrueFalse ; got is set by getrandom() on glibc
if (!got) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
got = read(fd, &k, sizeof(k)) == (ssize_t)sizeof(k);
close(fd);
}
}
#endif
if (!got) {
/* last resort: time and addresses, at least different per run */
uint64_t t = (uint64_t)time(NULL), c = (uint64_t)clock();
k = t * 0x9E3779B97F4A7C15ULL ^ (c << 21) ^ (uint64_t)(uintptr_t)&k ^ (uint64_t)(uintptr_t)&_ht_process_key;
}
_ht_process_key = k;
}
/* splitmix64: spread a counter into well-mixed 64-bit values. */
static uint64_t _ht_mix(uint64_t x) {
x += 0x9E3779B97F4A7C15ULL;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
return x ^ (x >> 31);
}
/* A fresh, unpredictable seed for one table. */
static size_t _ht_new_seed(void) {
uint64_t n;
pthread_once(&_ht_key_once, _ht_key_init);
pthread_mutex_lock(&_ht_counter_lock);
n = ++_ht_table_counter;
pthread_mutex_unlock(&_ht_counter_lock);
return (size_t)_ht_mix(_ht_process_key ^ _ht_mix(n));
}
HASH_TABLE* new_ht(size_t size) {
HASH_TABLE* table = NULL;
if (size < 1) {
n_log(LOG_ERR, "Invalid size %zu for new_ht()", size);
return NULL;
}
Malloc(table, HASH_TABLE, 1);
__n_assert(table, n_log(LOG_ERR, "Error allocating HASH_TABLE *table"); return NULL);
table->size = size;
table->seed = _ht_new_seed();
table->nb_keys = 0;
errno = 0;
Malloc(table->hash_table, LIST*, size);
// table -> hash_table = (LIST **)calloc( size, sizeof( LIST *) );
__n_assert(table->hash_table, n_log(LOG_ERR, "Can't allocate table -> hash_table with size %zu !", size); Free(table); return NULL);
size_t it = 0;
for (it = 0; it < size; it++) {
table->hash_table[it] = new_generic_list(MAX_LIST_ITEMS);
// if no valid table then unroll previsouly created slots
if (!table->hash_table[it]) {
n_log(LOG_ERR, "Can't allocate table -> hash_table[ %zu ] !", it);
size_t it_delete = 0;
for (it_delete = 0; it_delete < it; it_delete++) {
list_destroy(&table->hash_table[it_delete]);
}
Free(table->hash_table);
Free(table);
return NULL;
}
}
table->mode = HASH_CLASSIC;
table->ht_put_int = _ht_put_int;
table->ht_put_double = _ht_put_double;
table->ht_put_ptr = _ht_put_ptr;
table->ht_put_string = _ht_put_string;
table->ht_put_string_ptr = _ht_put_string_ptr;
table->ht_get_int = _ht_get_int;
table->ht_get_double = _ht_get_double;
table->ht_get_string = _ht_get_string;
table->ht_get_ptr = _ht_get_ptr;
table->ht_get_node = _ht_get_node;
table->ht_remove = _ht_remove;
table->ht_search = _ht_search;
table->empty_ht = _empty_ht;
table->destroy_ht = _destroy_ht;
table->ht_print = _ht_print;
return table;
} /* new_ht(...) */
HASH_NODE* ht_get_node(HASH_TABLE* table, const char* key) {
__n_assert(table, return NULL);
__n_assert(key, return NULL);
return table->ht_get_node(table, key);
} /*ht_get_node(...) */
int ht_get_double(HASH_TABLE* table, const char* key, double* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_get_double(table, key, val);
} /* ht_get_double(...) */
int ht_get_int(HASH_TABLE* table, const char* key, HASH_INT_TYPE* val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_get_int(table, key, val);
} /* ht_get_int(...) */
int ht_get_ptr(HASH_TABLE* table, const char* key, void** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_get_ptr(table, key, val);
} /* ht_get_ptr(...) */
int ht_get_string(HASH_TABLE* table, const char* key, char** val) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_get_string(table, key, val);
} /* ht_get_string(...) */
int ht_put_double(HASH_TABLE* table, const char* key, double value) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_put_double(table, key, value);
} /* ht_put_double(...) */
int ht_put_int(HASH_TABLE* table, const char* key, HASH_INT_TYPE value) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_put_int(table, key, value);
} /* ht_put_int(...) */
int ht_put_ptr(HASH_TABLE* table, const char* key, void* ptr, void (*destructor)(void* ptr), void* (*duplicator)(void* ptr)) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_put_ptr(table, key, ptr, destructor, duplicator);
} /* ht_put_ptr(...) */
int ht_put_string(HASH_TABLE* table, const char* key, char* string) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_put_string(table, key, string);
} /* ht_put_string(...) */
int ht_put_string_ptr(HASH_TABLE* table, const char* key, char* string) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_put_string_ptr(table, key, string);
} /* ht_put_string_ptr(...) */
int ht_remove(HASH_TABLE* table, const char* key) {
__n_assert(table, return FALSE);
__n_assert(key, return FALSE);
return table->ht_remove(table, key);
} /* ht_remove(...) */
void ht_print(HASH_TABLE* table) {
__n_assert(table, return);
table->ht_print(table);
return;
} /* ht_print(...) */
LIST* ht_search(HASH_TABLE* table, int (*node_is_matching)(HASH_NODE* node)) {
__n_assert(table, return NULL);
return table->ht_search(table, node_is_matching);
} /* ht_search(...) */
int empty_ht(HASH_TABLE* table) {
__n_assert(table, return FALSE);
return table->empty_ht(table);
} /* empty_ht(...) */
int destroy_ht(HASH_TABLE** table) {
__n_assert((*table), return FALSE);
return (*table)->destroy_ht(table);
} /* destroy_ht(...) */
HASH_NODE* ht_get_node_ex(HASH_TABLE* table, HASH_VALUE hash_value) {
__n_assert(table, return NULL);
__n_assert(table->mode == HASH_CLASSIC, return NULL);
size_t index = (hash_value) % (table->size);
if (!table->hash_table[index]->start) {
return NULL;
}
list_foreach(list_node, table->hash_table[index]) {
HASH_NODE* node_ptr = (HASH_NODE*)list_node->ptr;
if (hash_value == node_ptr->hash_value) {
return node_ptr;
}
}
return NULL;
} /* ht_get_node_ex() */
int ht_get_ptr_ex(HASH_TABLE* table, HASH_VALUE hash_value, void** val) {
__n_assert(table, return FALSE);
__n_assert(table->mode == HASH_CLASSIC, return FALSE);
HASH_NODE* node = ht_get_node_ex(table, hash_value);
if (!node)
return FALSE;
if (node->type != HASH_PTR) {
n_log(LOG_ERR, "Can't get key[\"%zu\"] of type HASH_PTR, key is type %s", hash_value, ht_node_type(node));
return FALSE;
}
(*val) = node->data.ptr;
return TRUE;
} /* ht_get_ptr_ex() */
int ht_put_ptr_ex(HASH_TABLE* table, HASH_VALUE hash_value, void* val, void (*destructor)(void* ptr), void* (*duplicator)(void* ptr)) {
__n_assert(table, return FALSE);
__n_assert(table->mode == HASH_CLASSIC, return FALSE);
size_t index = 0;
HASH_NODE* new_hash_node = NULL;
HASH_NODE* node_ptr = NULL;
index = (hash_value) % (table->size);
/* we have some nodes here. Let's check if the key already exists */
list_foreach(list_node, table->hash_table[index]) {
node_ptr = (HASH_NODE*)list_node->ptr;
/* if we found the same key we just replace the value and return */
if (hash_value == node_ptr->hash_value) {
/* let's check the key isn't already assigned with another data type */
if (node_ptr->type == HASH_PTR) {
if (node_ptr->destroy_func && node_ptr->data.ptr) {
node_ptr->destroy_func(node_ptr->data.ptr);
} else if (!node_ptr->destroy_func && node_ptr->data.ptr) {
n_log(LOG_ERR, "Can't free previous key[\"%s\"] with type HASH_PTR , no hash node destroy func", node_ptr->key);
}
node_ptr->destroy_func = destructor;
node_ptr->duplicate_func = duplicator;
node_ptr->data.ptr = val;
return TRUE;
}
n_log(LOG_ERR, "Can't add key[\"%s\"] with type HASH_PTR , key already exist with type %s", node_ptr->key, ht_node_type(node_ptr));
return FALSE; /* key registered with another data type */
}
}
Malloc(new_hash_node, HASH_NODE, 1);
__n_assert(new_hash_node, n_log(LOG_ERR, "Could not allocate new_hash_node"); return FALSE);
new_hash_node->key = NULL;
new_hash_node->hash_value = hash_value;
new_hash_node->data.ptr = val;
new_hash_node->type = HASH_PTR;
new_hash_node->destroy_func = destructor;
new_hash_node->duplicate_func = duplicator;
table->nb_keys++;
return list_push(table->hash_table[index], new_hash_node, &_ht_node_destroy);
} /* ht_put_ptr_ex() */
int ht_remove_ex(HASH_TABLE* table, HASH_VALUE hash_value) {
__n_assert(table, return FALSE);
__n_assert(table->mode == HASH_CLASSIC, return FALSE);
size_t index = 0;
HASH_NODE* node_ptr = NULL;
LIST_NODE* node_to_kill = NULL;
index = (hash_value) % (table->size);
if (!table->hash_table[index]->start) {
n_log(LOG_ERR, "Can't remove key[\"%zu\"], table is empty", hash_value);
return FALSE;
}
list_foreach(list_node, table->hash_table[index]) {
node_ptr = (HASH_NODE*)list_node->ptr;
/* if we found the same */
if (hash_value == node_ptr->hash_value) {
node_to_kill = list_node;
break;
}
}
if (node_to_kill) {
node_ptr = remove_list_node(table->hash_table[index], node_to_kill, HASH_NODE);
_ht_node_destroy(node_ptr);
table->nb_keys--;
return TRUE;
}
n_log(LOG_ERR, "Can't delete key[\"%zu\"]: inexisting key", hash_value);
return FALSE;
} /* ht_remove_ex() */
LIST* ht_get_completion_list(HASH_TABLE* table, const char* keybud, size_t max_results) {
__n_assert(table, return NULL);
__n_assert(keybud, return NULL);
LIST* results = new_generic_list(max_results);
if (table->mode == HASH_TRIE) {
HASH_NODE* node = _ht_get_node_trie(table, keybud);
if (node) {
if (list_push(results, strdup(keybud), &free) == TRUE) {
_ht_depth_first_search(node, results);
}
} else {
node = table->root;
for (size_t it = 0; it < table->alphabet_length; it++) {
if (node->children[it]) {
char new_keybud[3] = "";
new_keybud[0] = (char)(it + table->alphabet_offset);
list_push(results, strdup(new_keybud), &free);
}
}
}
} else if (table->mode == HASH_CLASSIC) {
ht_foreach(node, table) {
HASH_NODE* hnode = (HASH_NODE*)node->ptr;
if (strncasecmp(keybud, hnode->key, strlen(keybud)) == 0) {
char* key = strdup(hnode->key);
if (list_push(results, key, &free) == FALSE) {
n_log(LOG_ERR, "not enough space in list or memory error, key %s not pushed !", key);
Free(key);
}
}
}
} else {
n_log(LOG_ERR, "unsupported mode %d", table->mode);
list_destroy(&results);
return NULL;
}
if (results && results->nb_items < 1)
list_destroy(&results);
return results;
} /* ht_get_completion_list(...) */
int is_prime(size_t nb) {
/* quick test for first primes */
if (nb <= 1) return FALSE;
if (nb <= 3) return TRUE;
/* skip middle five numbers in below loop */
if ((nb % 2 == 0) || (nb % 3 == 0))
return FALSE;
/* looping */
for (size_t it = 5; it * it <= nb; it = it + 6) {
if ((nb % it == 0) || (nb % (it + 2) == 0))
return FALSE;
}
return TRUE;
} /* is_prime() */
size_t next_prime(size_t nb) {
if (nb <= 1)
return 2;
size_t next_prime = nb;
do {
next_prime++;
} while (is_prime(next_prime) == FALSE);
return next_prime;
} /* next_prime() */
int ht_get_table_collision_percentage(HASH_TABLE* table) {
__n_assert(table, return FALSE);
if (table->mode != HASH_CLASSIC) {
n_log(LOG_ERR, "unsupported table->mode (%d instead or %d)", table->mode, HASH_CLASSIC);
return FALSE;
}
if (table->size == 0) return FALSE;
size_t nb_collisionned_lists = 0;
for (size_t hash_it = 0; hash_it < table->size; hash_it++) {
if (table->hash_table[hash_it] && table->hash_table[hash_it]->nb_items > 1) {
nb_collisionned_lists++;
}
}
size_t collision_percentage = (100 * nb_collisionned_lists) / table->size;
return (int)collision_percentage;
} /* ht_get_table_collision_percentage() */
size_t ht_get_optimal_size(HASH_TABLE* table) {
__n_assert(table, return 0);
if (table->mode != HASH_CLASSIC) {
n_log(LOG_ERR, "unsupported table->mode (%d instead or %d)", table->mode, HASH_CLASSIC);
return FALSE;
}
size_t optimum_size = (size_t)((double)table->nb_keys * 1.3);
if (is_prime(optimum_size) != TRUE)
optimum_size = next_prime(optimum_size);
return optimum_size;
} /* ht_get_optimal_size() */
int ht_resize(HASH_TABLE** table, size_t size) {
__n_assert((*table), return FALSE);
if ((*table)->mode != HASH_CLASSIC) {
n_log(LOG_ERR, "unsupported table->mode (%d instead or %d)", (*table)->mode, HASH_CLASSIC);
return FALSE;
}
if (size < 1) {
n_log(LOG_ERR, "invalid size %zu for hash table %p", size, (void*)(*table));
return FALSE;
}
/* mark every existing entry for rehash before resizing.
* Using ht_foreach (plain for-loop macro) instead of HT_FOREACH
* because the body needs no break/continue, and HT_FOREACH's
* GCC nested-function expansion is rejected by clang's parser. */
ht_foreach(it_node, (*table)) {
HASH_NODE* node = (HASH_NODE*)it_node->ptr;
node->need_rehash = 1;
}
if (size > (*table)->size) {
if (Realloc((*table)->hash_table, LIST*, size) == FALSE) {
return FALSE;
}
for (size_t it = (*table)->size; it < size; it++) {
(*table)->hash_table[it] = new_generic_list(MAX_LIST_ITEMS);
if (!(*table)->hash_table[it]) {
n_log(LOG_ERR, "Can't allocate table -> hash_table[ %zu ] !", it);
/* destroy newly added slots and shrink back */
for (size_t it_delete = (*table)->size; it_delete < it; it_delete++) {
list_destroy(&(*table)->hash_table[it_delete]);
}
/* best-effort rollback: shrink back to original size.
Realloc preserves the pointer on failure, which is fine here. */
Realloc((*table)->hash_table, LIST*, (*table)->size);
return FALSE;
}
}
/* rehash all marked nodes into their new positions */
for (size_t it = 0; it < size; it++) {
if ((*table)->hash_table[it]) {
while ((*table)->hash_table[it]->start) {
HASH_NODE* hash_node = (HASH_NODE*)(*table)->hash_table[it]->start->ptr;
if (hash_node->need_rehash == 0)
break;
hash_node->need_rehash = 0;
LIST_NODE* node = list_node_shift((*table)->hash_table[it]);
node->next = node->prev = NULL;
size_t index = (hash_node->hash_value) % (size);
list_node_push((*table)->hash_table[index], node);
}
}
}
} else {
/* rehash nodes that need it into the smaller index space */
for (size_t it = 0; it < (*table)->size; it++) {
if ((*table)->hash_table[it]) {
while ((*table)->hash_table[it]->start) {
HASH_NODE* hash_node = (HASH_NODE*)(*table)->hash_table[it]->start->ptr;
if (hash_node->need_rehash == 0)
break;
hash_node->need_rehash = 0;
LIST_NODE* node = list_node_shift((*table)->hash_table[it]);
node->next = node->prev = NULL;
size_t index = (hash_node->hash_value) % (size);
list_node_push((*table)->hash_table[index], node);
}
}
}
/* destroy trailing slots that are now unused */
for (size_t it = size; it < (*table)->size; it++) {
list_destroy(&(*table)->hash_table[it]);
}
/* shrink the backing array; Realloc preserves the pointer on failure.
Trailing slots have already been list_destroy'd so they are NULL.
(*table)->size is updated below regardless, keeping the table
consistent even if the allocator cannot shrink in-place. */
Realloc((*table)->hash_table, LIST*, size);
}
(*table)->size = size;
return TRUE;
} /* ht_resize() */
int ht_optimize(HASH_TABLE** table) {
__n_assert((*table), return FALSE);
if ((*table)->mode != HASH_CLASSIC) {
n_log(LOG_ERR, "unsupported table->mode (%d instead or %d)", (*table)->mode, HASH_CLASSIC);
return FALSE;
}
size_t optimal_size = ht_get_optimal_size((*table));
if (optimal_size == FALSE) {
return FALSE;
}
int collision_percentage = ht_get_table_collision_percentage((*table));
if (collision_percentage == FALSE)
return FALSE;
int resize_result = ht_resize(table, optimal_size);
if (resize_result == FALSE) {
return FALSE;
}
collision_percentage = ht_get_table_collision_percentage((*table));
if (collision_percentage == FALSE) {
return FALSE;
}
return TRUE;
} /* ht_optimize() */
HASH_TABLE* ht_duplicate(HASH_TABLE* table) {
__n_assert(table, return NULL);
HASH_TABLE* duplicated_table = NULL;
if (table->mode != HASH_CLASSIC) {
n_log(LOG_ERR, "unsupported mode %d for table %p", table->mode, table);
return NULL;
}
duplicated_table = new_ht(table->size);
if (!duplicated_table) {
n_log(LOG_ERR, "couldn't allocate duplicated table of %zu elements", table->size);
return NULL;
}
ht_foreach(node, table) {
HASH_NODE* hash_node = (HASH_NODE*)node->ptr;
int has_succeeded = TRUE;
switch (hash_node->type) {
case HASH_INT:
has_succeeded = ht_put_int(duplicated_table, hash_node->key, hash_node->data.ival);
break;
case HASH_DOUBLE:
has_succeeded = ht_put_double(duplicated_table, hash_node->key, hash_node->data.fval);
break;
case HASH_PTR: {
if (hash_node->duplicate_func && hash_node->data.ptr) {
/* deep copy: new entry takes ownership with the same funcs */
void* duplicated_ptr = hash_node->duplicate_func(hash_node->data.ptr);
if (!duplicated_ptr) {
n_log(LOG_ERR, "duplicate_func returned NULL for key [%s]", hash_node->key);
has_succeeded = FALSE;
break;
}
has_succeeded = ht_put_ptr(duplicated_table, hash_node->key, duplicated_ptr, hash_node->destroy_func, hash_node->duplicate_func);
} else {
/* shallow copy: pass NULL destroy/duplicate so the duplicate table
does not take ownership; only the source table will free the pointer */
has_succeeded = ht_put_ptr(duplicated_table, hash_node->key, hash_node->data.ptr, NULL, NULL);
}
} break;
case HASH_STRING:
has_succeeded = ht_put_string(duplicated_table, hash_node->key, hash_node->data.string);
break;
default:
n_log(LOG_ERR, "unknown node type %d for key [%s], skipping", hash_node->type, hash_node->key);
break;
}
if (has_succeeded == FALSE) {
n_log(LOG_ERR, "problem when trying to duplicate value in %p, duplication cancelled", table);
destroy_ht(&duplicated_table);
return NULL;
}
}
return duplicated_table;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB