Files
m241Neelima/components/ip_net2-6.9/ipcom/src/ipcom_hash.c
T
2026-07-30 11:19:44 +05:30

495 lines
14 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_hash.c,v $ $Revision: 1.39.10.1 $
* $Source: /home/interpeak/CVSRoot/ipcom/src/ipcom_hash.c,v $
* $Author: svc-cmnet $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/* IPCOM hash table implementation */
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipcom_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include "ipcom_clib.h"
#include "ipcom_cstyle.h"
#include "ipcom_err.h"
#include "ipcom_hash.h"
#include "ipcom_syslog.h"
#include "ipcom_type.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/* The hash table will shrink when less populated that this value (in percent) */
#define IPCOM_HASH_LOW_WATER_MARK 2
/* The hash table will be doubled in size when more populated than this value
(in percent) */
#define IPCOM_HASH_HIGH_WATER_MARK 25
/* Size of the hash table when it is initially created. */
#define IPCOM_HASH_INITIAL_SIZE 29
#define IPCOM_SYSLOG_PRIORITY IPCOM_SYSLOG_IPCOM_PRIORITY
#define IPCOM_SYSLOG_FACILITY IPCOM_LOG_IPCOM
/* Default memory allocation routines (static memory) */
#ifndef IPCOM_MALLOC_STATIC
#define IPCOM_MALLOC_STATIC ipcom_malloc
#endif
#ifndef IPCOM_CALLOC_STATIC
#define IPCOM_CALLOC_STATIC ipcom_calloc
#endif
#ifndef IPCOM_FREE_STATIC
#define IPCOM_FREE_STATIC ipcom_free
#endif
/* Default memory allocation routines (dynamic memory) */
#ifndef IPCOM_MALLOC_DYNAMIC
#define IPCOM_MALLOC_DYNAMIC ipcom_malloc
#endif
#ifndef IPCOM_FREE_DYNAMIC
#define IPCOM_FREE_DYNAMIC ipcom_free
#endif
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipcom_hash_resize
*===========================================================================
* Description: Doubles the size or cuts the size in half.
* Parameters: head - The head of the hash table.
* increase - Set to IP_TRUE to double the size, IP_FALSE
* to half the size.
* Returns: IPCOM_SUCCESS or IPCOM_ERR_NO_MEMORY if not enough memory
* could be allocated.
*
*/
IP_STATIC Ip_err
ipcom_hash_resize(Ipcom_hash *head, Ip_bool increase)
{
void **old_hash_table;
unsigned old_size;
unsigned i;
Ip_err err;
old_hash_table = head->table;
old_size = head->size;
/* Keep the size odd since that will increase the chance of the high
bits of the hash value to affect the low bits which is used as
index in the hash table */
if (increase)
head->size = (head->size << 1) + 3;
else
head->size = (head->size >> 1) - 1;
head->table = IPCOM_CALLOC_STATIC(head->size, sizeof(*head->table));
if (head->table == IP_NULL)
{
IPCOM_LOG2(ERR, "ipcom_hash_resize :: ipcom_calloc(%d, %d) failed",
head->size,
sizeof(*head->table));
head->size = old_size;
head->table = old_hash_table;
return IPCOM_ERR_NO_MEMORY;
}
head->elem = 0;
for (i = 0; i < old_size; i++)
if (old_hash_table[i] != IP_NULL)
{
err = ipcom_hash_add(head, old_hash_table[i]);
ip_assert(err == IPCOM_SUCCESS);
(void)err;
}
IPCOM_FREE_STATIC(old_hash_table);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipcom_hash_can_move_obj
*===========================================================================
* Description: Returns if the object with the specified ideal index can
* be move to the specified new index.
* Parameters: ideal_index - The index the object would have the table had
* no collisions.
* new_index - The new index this object would be moved to.
* size - The size of the current hash array.
* Returns: IP_TRUE if the object can be moved.
*
*/
IP_STATIC Ip_bool
ipcom_hash_can_move_obj(unsigned ideal_index, unsigned new_index, unsigned size)
{
unsigned half_size = (size >> 1);
#if IPCOM_HASH_HIGH_WATER_MARK > 50
/* NOTE: this algorithm will break if IPCOM_HASH_HIGH_WATER_MARK
is 50 or higher */
IP_PANIC();
#endif
if (IP_ABS((int) (ideal_index - new_index)) > (int)half_size)
{
ideal_index = (ideal_index + half_size) % size;
new_index = (new_index + half_size) % size;
}
return ideal_index <= new_index;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipcom_hash_new
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ipcom_hash *
ipcom_hash_new(Ipcom_hash_obj_func obj_hash_func,
Ipcom_hash_key_func key_hash_func,
Ipcom_hash_cmp_func obj_key_cmp)
{
Ipcom_hash *head;
ip_assert(obj_hash_func != IP_NULL);
ip_assert(key_hash_func != IP_NULL);
ip_assert(obj_key_cmp != IP_NULL);
head = IPCOM_MALLOC_STATIC(sizeof(*head));
if (head == IP_NULL)
return IP_NULL;
head->table = IPCOM_CALLOC_STATIC(IPCOM_HASH_INITIAL_SIZE, sizeof(*head->table));
if (head->table == IP_NULL)
{
IPCOM_FREE_STATIC(head);
return IP_NULL;
}
head->obj_hash_func = obj_hash_func;
head->key_hash_func = key_hash_func;
head->obj_key_cmp = obj_key_cmp;
head->size = IPCOM_HASH_INITIAL_SIZE;
head->elem = 0;
return head;
}
/*
*===========================================================================
* ipcom_hash_delete
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC void
ipcom_hash_delete(Ipcom_hash *head)
{
if (head == IP_NULL)
return;
head->size = 0;
IPCOM_FREE_STATIC(head->table);
IPCOM_FREE_STATIC(head);
}
/*
*===========================================================================
* ipcom_hash_add
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipcom_hash_add(Ipcom_hash *head, void *obj)
{
unsigned hash_index;
Ip_err err;
ip_assert(head->size > 0);
if ((head->elem + 1) * 100 > IPCOM_HASH_HIGH_WATER_MARK * head->size)
{
err = ipcom_hash_resize(head, IP_TRUE);
if (err != IPCOM_SUCCESS)
return err;
}
if (head->size / 2 <= head->elem)
/* The size of the hash table is locked and it has reached
the capacity limit */
return IPCOM_ERR_REACHED_MAX;
hash_index = head->obj_hash_func(obj) % head->size;
/* Find an empty slot at the hash index or after it if another
object hash:ed to the same value */
while (head->table[hash_index % head->size] != IP_NULL)
{
if (head->table[hash_index % head->size] == obj)
return IPCOM_ERR_DUPLICATE;
hash_index++;
}
head->table[hash_index % head->size] = obj;
head->elem++;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipcom_hash_remove
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipcom_hash_remove(Ipcom_hash *head, void *obj)
{
void *cobj;
unsigned hash_index;
unsigned new_index;
hash_index = head->obj_hash_func(obj) % head->size;
while (head->table[hash_index] != obj)
{
if (head->table[hash_index] == IP_NULL)
/* The object does not exist */
return IPCOM_ERR_NOT_FOUND;
hash_index++;
hash_index = hash_index % head->size;
}
/* Object found, remove it */
head->table[hash_index] = IP_NULL;
head->elem--;
if (head->size > IPCOM_HASH_INITIAL_SIZE &&
head->elem * 100 / head->size < IPCOM_HASH_LOW_WATER_MARK)
{
/* Half the size of the hash table */
if (ipcom_hash_resize(head, IP_FALSE) == IPCOM_SUCCESS)
/* No need to fix collisions since the hash table was regenerated */
return IPCOM_SUCCESS;
}
new_index = hash_index;
/* The following object(s) can be out-of-place due to collisions, check them */
while ((cobj = head->table[++hash_index % head->size]) != IP_NULL)
{
unsigned ideal_index = head->obj_hash_func(cobj) % head->size;
hash_index %= head->size;
if (ipcom_hash_can_move_obj(ideal_index, new_index, head->size))
{
/* Move this object to the hole from the removed or last moved object */
head->table[new_index] = cobj;
head->table[hash_index] = IP_NULL;
new_index = hash_index;
}
/* else: the object is already at the correct place */
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipcom_hash_get
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC void *
ipcom_hash_get(Ipcom_hash *head, const void *key)
{
unsigned hash_index;
void *obj;
hash_index = head->key_hash_func(key) % head->size;
while ((obj = head->table[hash_index]) != IP_NULL)
{
if (head->obj_key_cmp(obj, key))
return obj;
hash_index = (hash_index + 1) % head->size;
}
return IP_NULL;
}
/*
*===========================================================================
* ipcom_hash_for_each
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC void
ipcom_hash_for_each(Ipcom_hash *head, Ipcom_hash_foreach_cb_func cb_func, void *data)
{
unsigned i;
unsigned x;
void **elems;
if (head->elem == 0)
return;
elems = IPCOM_MALLOC_DYNAMIC(head->elem * sizeof(void*)); /*lint !e647 */
if (elems == IP_NULL)
{
IP_PANIC();
return;
}
for (i = 0, x = 0; i < head->size; i++)
if (head->table[i] != IP_NULL)
elems[x++] = head->table[i];
ip_assert(x == head->elem);
for (i = 0; i < x; i++)
cb_func(elems[i], data);
IPCOM_FREE_DYNAMIC(elems);
}
/*
*===========================================================================
* ipcom_hash_update
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
/*lint -efunc(616,ipcom_hash_update) Control flows into case/default */
unsigned
ipcom_hash_update(const void *key_param, int key_len, unsigned initval)
{
unsigned a, b;
unsigned len;
const Ip_u8 *key = key_param;
/* Set up the internal state */
len = (unsigned) key_len;
a = initval;
b = len;
while (len >= 4)
{
a += (unsigned) (key[0] + (key[1] << 8) + (key[2] << 16) + (key[3] << 24));
b += (a << 3);
a += ~(b << 11);
b ^= (a >> 16);
key += 4; len -= 4;
}
/* All the case statements fall through */
switch (len)
{
case 3: a += (unsigned)(key[2] << 16);
case 2: b ^= (unsigned)(key[1] << 8);
case 1: a += key[0];
default: break;
}
a ^= (b >> 6);
b += ~(a << 15);
a ^= (b >> 10);
return a + b;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/