Files
ResInsight/ThirdParty/Ert/lib/util/hash.cpp
T
Magne Sjaastad 04091ad77d #4266 Update libecl
Use commit 0e1e780fd6f18ce93119061e36a4fca9711bc020

Excluded multibuild folder, as this caused git issues
2019-05-09 08:40:32 +02:00

860 lines
23 KiB
C++

/*
Copyright (C) 2011 Equinor ASA, Norway.
The file 'hash.c' is part of ERT - Ensemble based Reservoir Tool.
ERT 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 3 of the License, or
(at your option) any later version.
ERT 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 at <http://www.gnu.org/licenses/gpl.html>
for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <ert/util/hash.hpp>
#include <ert/util/hash_sll.hpp>
#include <ert/util/hash_node.hpp>
#include <ert/util/node_data.hpp>
#include <ert/util/util.h>
#include <ert/util/stringlist.hpp>
#ifdef __cplusplus
extern "C" {
#endif
#define HASH_DEFAULT_SIZE 16
#define HASH_TYPE_ID 771065
/**
This is **THE** hash function - which actually does the hashing.
*/
static uint32_t hash_index(const char *key, size_t len) {
uint32_t hash = 0;
size_t i;
for (i=0; i < len; i++) {
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
struct hash_struct {
UTIL_TYPE_ID_DECLARATION;
uint32_t size; /* This is the size of the internal table **NOT**NOT** the number of elements in the table. */
uint32_t elements; /* The number of elements in the hash table. */
double resize_fill;
hash_sll_type **table;
hashf_type *hashf;
};
typedef struct hash_sort_node {
char *key;
int cmp_value;
} hash_sort_type;
/*****************************************************************/
/* Low level access functions */
/*****************************************************************/
static void * __hash_get_node(const hash_type *__hash , const char *key, bool abort_on_error) {
hash_type * hash = (hash_type *) __hash; /* The net effect is no change - but .... ?? */
hash_node_type * node = NULL;
{
const uint32_t global_index = hash->hashf(key , strlen(key));
const uint32_t table_index = (global_index % hash->size);
node = hash_sll_get(hash->table[table_index] , global_index , key);
if (node == NULL && abort_on_error)
util_abort("%s: tried to get from key:%s which does not exist - aborting \n",__func__ , key);
}
return node;
}
static node_data_type * hash_get_node_data(const hash_type *hash , const char *key) {
hash_node_type * node = (hash_node_type*)__hash_get_node(hash , key , true);
return hash_node_get_data(node);
}
/**
This function resizes the hash table when it has become to full.
The table only grows - this function is called from
__hash_insert_node().
If you know in advance (roughly) how large the hash table will be
it can be advantageous to call hash_resize() manually, to avoid
repeated internal calls to hash_resize().
*/
void hash_resize(hash_type *hash, int new_size) {
hash_sll_type ** new_table = hash_sll_alloc_table( new_size );
hash_node_type * node;
uint32_t i;
for (i=0; i < hash->size; i++) {
node = hash_sll_get_head(hash->table[i]);
while (node != NULL) {
uint32_t new_table_index = hash_node_set_table_index(node , new_size);
hash_node_type *next_node = hash_node_get_next(node);
hash_sll_add_node(new_table[new_table_index] , node);
node = next_node;
}
}
/*
Only freeing the table structure, *NOT* calling the node_free()
functions, which happens when hash_sll_free() is called.
*/
{
for (i=0; i < hash->size; i++)
free( hash->table[i] );
free( hash->table );
}
hash->size = new_size;
hash->table = new_table;
}
/**
This is the low-level function for inserting a hash node.
*/
static void __hash_insert_node(hash_type *hash , hash_node_type *node) {
uint32_t table_index = hash_node_get_table_index(node);
{
/*
If a node with the same key already exists in the table
it is removed.
*/
hash_node_type *existing_node = (hash_node_type*)__hash_get_node(hash , hash_node_get_key(node) , false);
if (existing_node != NULL) {
hash_sll_del_node(hash->table[table_index] , existing_node);
hash->elements--;
}
}
hash_sll_add_node(hash->table[table_index] , node);
hash->elements++;
if ((1.0 * hash->elements / hash->size) > hash->resize_fill)
hash_resize(hash , hash->size * 2);
}
/**
This function deletes a node from the hash_table.
*/
static void hash_del__(hash_type *hash , const char *key) {
const uint32_t global_index = hash->hashf(key , strlen(key));
const uint32_t table_index = (global_index % hash->size);
hash_node_type *node = hash_sll_get(hash->table[table_index] , global_index , key);
if (node == NULL)
util_abort("%s: hash does not contain key:%s - aborting \n",__func__ , key);
else
hash_sll_del_node(hash->table[table_index] , node);
hash->elements--;
}
/**
This functions takes a hash_node and finds the "next" hash node by
traversing the internal hash structure. Should NOT be confused with
the other functions providing iterations to user-space.
*/
static hash_node_type * hash_internal_iter_next(const hash_type *hash , const hash_node_type * node) {
hash_node_type *next_node = hash_node_get_next(node);
if (next_node == NULL) {
const uint32_t table_index = hash_node_get_table_index(node);
if (table_index < hash->size) {
uint32_t i = table_index + 1;
while (i < hash->size && hash_sll_empty(hash->table[i]))
i++;
if (i < hash->size)
next_node = hash_sll_get_head(hash->table[i]);
}
}
return next_node;
}
/**
This is the low level function which traverses a hash table and
allocates a char ** list of keys.
If the hash table is empty NULL is returned.
*/
static char ** hash_alloc_keylist__(const hash_type *hash) {
char **keylist;
if (hash->elements > 0) {
int i = 0;
hash_node_type *node = NULL;
keylist = (char**)calloc(hash->elements , sizeof *keylist);
{
uint32_t i = 0;
while (i < hash->size && hash_sll_empty(hash->table[i]))
i++;
if (i < hash->size)
node = hash_sll_get_head(hash->table[i]);
}
while (node != NULL) {
const char *key = hash_node_get_key(node);
keylist[i] = util_alloc_string_copy(key);
node = hash_internal_iter_next(hash , node);
i++;
}
} else
keylist = NULL;
return keylist;
}
/*****************************************************************/
/**
The fundamental functions above relate the hash_node
structure. Here comes a list of functions for inserting managed
copies of various types.
*/
void hash_insert_string(hash_type * hash , const char * key , const char * value) {
node_data_type * node_data = node_data_alloc_string( value );
hash_node_type * hash_node = hash_node_alloc_new(key , node_data , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
char * hash_get_string(const hash_type * hash , const char * key) {
node_data_type * node_data = hash_get_node_data(hash , key);
return node_data_get_string( node_data );
}
void hash_insert_int(hash_type * hash , const char * key , int value) {
node_data_type * node_data = node_data_alloc_int( value );
hash_node_type * hash_node = hash_node_alloc_new(key , node_data , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
int hash_get_int(const hash_type * hash , const char * key) {
node_data_type * node_data = hash_get_node_data(hash , key);
return node_data_get_int( node_data );
}
/**
Small wrapper around hash_get_int() / hash_insert_int() which
implements a zero based counter.
hash_inc_counter()
Will increment the integer value stored in the node_data instance,
and return the updated value. If the key is not present in the hash
it will be inserted as an integer with value 0, and 0 will be
returned.
*/
int hash_inc_counter(hash_type * hash , const char * counter_key) {
if (hash_has_key( hash , counter_key)) {
node_data_type * node_data = hash_get_node_data(hash , counter_key);
return node_data_fetch_and_inc_int( node_data );
} else {
hash_insert_int(hash , counter_key , 0);
return 0;
}
}
/**
Will return 0 if the key is not in the hash.
*/
int hash_get_counter(const hash_type * hash , const char * key) {
if (hash_has_key( hash , key ))
return hash_get_int( hash , key );
else
return 0;
}
void hash_insert_double(hash_type * hash , const char * key , double value) {
node_data_type * node_data = node_data_alloc_double( value );
hash_node_type * hash_node = hash_node_alloc_new(key , node_data , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
double hash_get_double(const hash_type * hash , const char * key) {
node_data_type * node_data = hash_get_node_data(hash , key);
return node_data_get_double( node_data );
}
/*****************************************************************/
void hash_del(hash_type *hash , const char *key) {
hash_del__(hash , key);
}
/**
This function will delete the key if it exists in the hash, but it
will NOT fail if the key is not already in the hash table.
*/
void hash_safe_del(hash_type * hash , const char * key) {
if (__hash_get_node(hash , key , false))
hash_del__(hash , key);
}
void hash_clear(hash_type *hash) {
int old_size = hash_get_size(hash);
if (old_size > 0) {
char **keyList = hash_alloc_keylist__( hash );
for (int i=0; i < old_size; i++) {
hash_del__(hash , keyList[i]);
free(keyList[i]);
}
free(keyList);
}
}
void * hash_get(const hash_type *hash , const char *key) {
hash_node_type * hash_node = (hash_node_type*)__hash_get_node(hash , key , true);
node_data_type * data_node = (node_data_type*)hash_node_get_data( hash_node );
return node_data_get_ptr( data_node );
}
/**
This function will return NULL if the hash does not
contain 'key'.
*/
void * hash_safe_get( const hash_type * hash , const char * key ) {
hash_node_type * hash_node = (hash_node_type*)__hash_get_node(hash , key , false);
if (hash_node != NULL) {
node_data_type * data_node = hash_node_get_data( hash_node );
return node_data_get_ptr( data_node );
} else
return NULL;
}
/**
This function will:
1. Return an object from the hash table.
2. Remove it from the table.
Observe that if the object has been installed with a destructor,
the object will be destroyed by the hash_del() operation, and the
return value is complete gibberish - i.e. this function can NOT be
used on hash-owned references.
*/
void * hash_pop( hash_type * hash , const char * key) {
void * value = hash_get( hash , key);
hash_del( hash , key );
return value;
}
/******************************************************************/
/* Allocators */
/******************************************************************/
static hash_type * __hash_alloc(int size, double resize_fill , hashf_type *hashf) {
hash_type* hash;
hash = (hash_type*)util_malloc(sizeof *hash );
UTIL_TYPE_ID_INIT(hash , HASH_TYPE_ID);
hash->size = size;
hash->hashf = hashf;
hash->table = hash_sll_alloc_table(hash->size);
hash->elements = 0;
hash->resize_fill = resize_fill;
return hash;
}
hash_type * hash_alloc() {
return __hash_alloc(HASH_DEFAULT_SIZE , 0.50 , hash_index);
}
UTIL_SAFE_CAST_FUNCTION( hash , HASH_TYPE_ID)
UTIL_SAFE_CAST_FUNCTION_CONST( hash , HASH_TYPE_ID)
UTIL_IS_INSTANCE_FUNCTION(hash , HASH_TYPE_ID)
void hash_free(hash_type *hash) {
uint32_t i;
for (i=0; i < hash->size; i++)
hash_sll_free(hash->table[i]);
free(hash->table);
free(hash);
}
void hash_free__(void * void_hash) {
hash_free(hash_safe_cast( void_hash ));
}
char ** hash_alloc_keylist(const hash_type *hash) {
return hash_alloc_keylist__(hash);
}
stringlist_type * hash_alloc_stringlist(const hash_type * hash) {
stringlist_type * stringlist = stringlist_alloc_new();
char ** keylist = hash_alloc_keylist__(hash);
int i;
for (i = 0; i < hash_get_size( hash ); i++) {
stringlist_append_copy( stringlist , keylist[i] );
free(keylist[i]);
}
free( keylist );
return stringlist;
}
/*****************************************************************/
/**
The standard functions for inserting an entry in the hash table:
hash_insert_copy(): The hash table uses copyc() to make a copy of
value, which is inserted in the table. This means that the
calling scope is free to do whatever it wants with value; and
is also responsible for freeing it. The hash_table takes
responsibility for freeing it's own copy.
hash_insert_hash_owned_ref(): The hash table takes ownership of
'value', in the sense that the hash table will call the
'destructor' del() on value when the node is deleted.
hash_insert_ref(): The hash table ONLY contains a pointer to
value; the calling scope retains full ownership to
value. When the hash node is deleted, the hash implementation
will just drop the reference on the floor.
*/
void hash_insert_copy(hash_type *hash , const char *key , const void *value , copyc_ftype *copyc , free_ftype *del) {
hash_node_type *hash_node;
if (copyc == NULL || del == NULL)
util_abort("%s: must provide copy constructer and delete operator for insert copy - aborting \n",__func__);
{
node_data_type * data_node = node_data_alloc_ptr( value , copyc , del );
hash_node = hash_node_alloc_new(key , data_node , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
}
/**
This function will insert a reference "value" with key "key"; when
the key is deleted - either by an explicit call to hash_del(), or
when the complete hash table is free'd with hash_free(), the
destructur 'del' is called with 'value' as argument.
It is importand to realize that when elements are inserted into a
hash table with this function the calling scope gives up
responsibility of freeing the memory pointed to by value.
*/
void hash_insert_hash_owned_ref(hash_type *hash , const char *key , const void *value , free_ftype *del) {
hash_node_type *hash_node;
if (del == NULL)
util_abort("%s: must provide delete operator for insert hash_owned_ref - aborting \n",__func__);
{
node_data_type * data_node = node_data_alloc_ptr( value , NULL , del );
hash_node = hash_node_alloc_new(key , data_node , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
}
void hash_insert_ref(hash_type *hash , const char *key , const void *value) {
hash_node_type *hash_node;
{
node_data_type * data_node = node_data_alloc_ptr( value , NULL , NULL);
hash_node = hash_node_alloc_new(key , data_node , hash->hashf , hash->size);
__hash_insert_node(hash , hash_node);
}
}
bool hash_has_key(const hash_type *hash , const char *key) {
if (__hash_get_node(hash , key , false) == NULL)
return false;
else
return true;
}
int hash_get_size(const hash_type *hash) {
return hash->elements;
}
/******************************************************************/
/**
Here comes a list of functions for allocating keylists which have
been sorted in various ways.
*/
static hash_sort_type * hash_alloc_sort_list(const hash_type *hash ,
const char **keylist) {
int i; hash_sort_type * sort_list = (hash_sort_type*)calloc(hash_get_size(hash) , sizeof * sort_list);
for (i=0; i < hash_get_size(hash); i++)
sort_list[i].key = util_alloc_string_copy(keylist[i]);
return sort_list;
}
static void hash_free_sort_list(const hash_type *hash , hash_sort_type *sort_list) {
int i;
for (i=0; i < hash_get_size(hash); i++)
free(sort_list[i].key);
free(sort_list);
}
static int hash_sortlist_cmp(const void *_p1 , const void *_p2) {
const hash_sort_type *p1 = (const hash_sort_type *) _p1;
const hash_sort_type *p2 = (const hash_sort_type *) _p2;
if (p1->cmp_value == p2->cmp_value)
return 0;
else if (p1->cmp_value < p2->cmp_value)
return -1;
else
return 1;
}
static char ** __hash_alloc_ordered_keylist(const hash_type *hash , int ( hash_get_cmp_value) (const void * )) {
int i;
char **sorted_keylist;
char **tmp_keylist = hash_alloc_keylist(hash);
hash_sort_type * sort_list = hash_alloc_sort_list(hash , (const char **) tmp_keylist);
for (i = 0; i < hash_get_size(hash); i++)
sort_list[i].cmp_value = hash_get_cmp_value( hash_get(hash , sort_list[i].key) );
qsort(sort_list , hash_get_size(hash) , sizeof *sort_list , &hash_sortlist_cmp);
sorted_keylist = (char**)calloc(hash_get_size(hash) , sizeof *sorted_keylist);
for (i = 0; i < hash_get_size(hash); i++) {
sorted_keylist[i] = util_alloc_string_copy(sort_list[i].key);
free(tmp_keylist[i]);
}
free(tmp_keylist);
hash_free_sort_list(hash , sort_list);
return sorted_keylist;
}
char ** hash_alloc_sorted_keylist(const hash_type *hash , int ( hash_get_cmp_value) (const void *)) {
char ** key_list;
key_list = __hash_alloc_ordered_keylist(hash , hash_get_cmp_value);
return key_list;
}
static int key_cmp(const void *_s1 , const void *_s2) {
const char * s1 = *((const char **) _s1);
const char * s2 = *((const char **) _s2);
return strcmp(s1 , s2);
}
static char ** __hash_alloc_key_sorted_list(const hash_type *hash, int (*cmp) (const void * , const void *)) {
char **keylist = hash_alloc_keylist(hash);
qsort(keylist , hash_get_size(hash) , sizeof *keylist , cmp);
return keylist;
}
char ** hash_alloc_key_sorted_list(const hash_type * hash, int (*cmp) (const void *, const void *))
{
char ** key_list;
key_list =__hash_alloc_key_sorted_list(hash , cmp);
return key_list;
}
bool hash_key_list_compare(const hash_type * hash1, const hash_type * hash2)
{
bool has_equal_keylist;
int i,size1, size2;
char **keylist1, **keylist2;
size1 = hash_get_size(hash1);
size2 = hash_get_size(hash2);
if(size1 != size2) return false;
keylist1 = hash_alloc_key_sorted_list(hash1, &key_cmp);
keylist2 = hash_alloc_key_sorted_list(hash2, &key_cmp);
has_equal_keylist = true;
for(i=0; i<size1; i++)
{
if(strcmp(keylist1[i],keylist2[i]) != 0)
{
has_equal_keylist = false;
break;
}
}
for(i=0; i<size1; i++) {
free( keylist1[i] );
free( keylist2[i] );
}
free( keylist1 );
free( keylist2 );
return has_equal_keylist;
}
/*****************************************************************/
/**
This function will take a list of strings of type:
["OPT1:Value1" , "MIN:0.0001" , "MAX:1.00" , "FILE:XX"]
and build a hash table where the element in front of ':' is used as
key, and the element behind the ':' is used as value. The value is
internalized as a (char *) pointer with no type conversion. In the
calling scope the values should be extracted with hash_get().
Strings which can not be interpreted as KEY:VALUE are simply
ignored.
*/
hash_type * hash_alloc_from_options(const stringlist_type * options) {
int num_options = stringlist_get_size( options );
hash_type * opt_hash = hash_alloc();
int iopt;
for (iopt = 0; iopt < num_options; iopt++) {
char * option;
char * value;
util_binary_split_string( stringlist_iget(options , iopt) , ":" , true , &option , &value);
if ((option != NULL) && (value != NULL))
hash_insert_hash_owned_ref( opt_hash , option , util_alloc_string_copy(value) , free);
// Warning: could not interpret string as KEY:VALUE - ignored
free(option);
free(value);
}
return opt_hash;
}
bool hash_add_option( hash_type * hash, const char * key_value) {
bool addOK = false;
{
char * value;
char * key;
util_binary_split_string( key_value , ":" , true , &key , &value);
if (value != NULL) {
hash_insert_hash_owned_ref( hash , key , value , free );
addOK = true;
}
free( key );
}
return addOK;
}
/*****************************************************************/
/**
This is a **VERY** simple iteration object.
Do **NOT** use with multi-threading.
*/
struct hash_iter_struct {
const hash_type * hash; /* The hash we are iterating over. */
char ** keylist; /* The keys in the hash table - at the moment of hash_iter_alloc(). */
int num_keys;
int current_key_num; /* This integer retains the state. */
};
void hash_iter_restart( hash_iter_type * iter ) {
iter->current_key_num = 0;
}
hash_iter_type * hash_iter_alloc(const hash_type * hash) {
hash_iter_type * iter = (hash_iter_type*)util_malloc(sizeof * iter );
iter->hash = hash;
iter->num_keys = hash_get_size(hash);
iter->keylist = hash_alloc_keylist( (hash_type *) hash);
hash_iter_restart( iter );
return iter;
}
void hash_iter_free(hash_iter_type * iter) {
util_free_stringlist(iter->keylist, iter->num_keys);
free(iter);
}
bool hash_iter_is_complete(const hash_iter_type * iter) {
if(iter->current_key_num == iter->num_keys)
return true;
else
return false;
}
/**
Get the next key.
Returns NULL if the iteration has ended.
*/
const char * hash_iter_get_next_key(hash_iter_type * iter) {
const char * key;
if(iter->current_key_num == iter->num_keys)
return NULL;
key = iter->keylist[iter->current_key_num];
iter->current_key_num++;
if(!hash_has_key(iter->hash, key))
util_abort("%s: Programming error. Using hash_iter with multi-threading??\n", __func__);
return key;
}
void * hash_iter_get_next_value(hash_iter_type * iter) {
const char * key = hash_iter_get_next_key(iter);
if(key != NULL)
return hash_get(iter->hash, key);
else
return NULL;
}
/*****************************************************************/
/**
This function will iterate through the hash table, and call the
function func on each of the elements in the table inplace:
....
value = hash_get( hash , key );
func( value ); <-- The call is inplace - with no arguments,
.... and no return value. The content of 'value'
can obviously change, but 'value' itself
must still be a valid reference!
*/
void hash_apply( hash_type * hash , hash_apply_ftype * func) {
hash_iter_type * iter = hash_iter_alloc( hash );
while (!hash_iter_is_complete( iter )) {
const char * key = hash_iter_get_next_key( iter );
void * value = hash_get( hash , key );
func( value );
}
hash_iter_free( iter );
}
#undef HASH_GET_SCALAR
#undef HASH_INSERT_SCALAR
#undef HASH_INSERT_ARRAY
#undef HASH_GET_ARRAY_PTR
#undef HASH_NODE_AS
#undef HASH_DEFAULT_SIZE
#ifdef __cplusplus
}
#endif