mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 22:50:25 -05:00
Use commit 0e1e780fd6f18ce93119061e36a4fca9711bc020 Excluded multibuild folder, as this caused git issues
860 lines
23 KiB
C++
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
|