2006-12-26 16:17:02 +00:00
/*
2008-04-02 15:24:51 +00:00
* Hash - table and - set data structures .
2006-12-26 16:17:02 +00:00
*
2008-04-02 15:24:51 +00:00
* Copyright ( C ) 2007 - 2008 Sourcefire , Inc .
*
* Authors : Török Edvin
2006-12-26 16:17:02 +00:00
*
* This program is free software ; you can redistribute it and / or modify
2008-04-02 15:24:51 +00:00
* it under the terms of the GNU General Public License version 2 as
2007-06-30 11:50:56 +00:00
* published by the Free Software Foundation .
2006-12-26 16:17:02 +00:00
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
# include <clamav-config.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
2007-02-01 11:50:29 +00:00
# include "cltypes.h"
2006-12-26 16:17:02 +00:00
# include "clamav.h"
# include "others.h"
# include "hashtab.h"
2008-02-06 18:53:23 +00:00
# define MODULE_NAME "hashtab: "
2006-12-26 16:17:02 +00:00
2008-01-21 15:52:21 +00:00
static const char DELETED_KEY [ ] = " " ;
2006-12-26 16:17:02 +00:00
2008-02-06 18:53:23 +00:00
static unsigned long nearest_power ( unsigned long num )
2006-12-26 16:17:02 +00:00
{
2008-02-06 18:53:23 +00:00
unsigned long n = 64 ;
while ( n < num ) {
n < < = 1 ;
if ( n = = 0 ) {
return num ;
}
2006-12-26 16:17:02 +00:00
}
2008-02-06 18:53:23 +00:00
return n ;
2006-12-26 16:17:02 +00:00
}
# ifdef PROFILE_HASHTABLE
/* I know, this is ugly, most of these functions get a const s, that gets its const-ness discarded,
* and then these functions modify something the compiler assumes is readonly .
* Please , never use PROFILE_HASHTABLE in production code , and in releases . Use it for development only ! */
2009-08-04 23:17:28 +02:00
static inline void PROFILE_INIT ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
memset ( & s - > PROFILE_STRUCT , 0 , sizeof ( s - > PROFILE_STRUCT ) ) ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_CALC_HASH ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . calc_hash + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_FIND_ELEMENT ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . find_req + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_FIND_NOTFOUND ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . not_found + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . not_found_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_FIND_FOUND ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . found + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . found_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_HASH_EXHAUSTED ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . hash_exhausted + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_GROW_START ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . grow + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_GROW_FOUND ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . grow_found + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . grow_found_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_GROW_DONE ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_DELETED_REUSE ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . deleted_reuse + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . deleted_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_INSERT ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . inserts + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . insert_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_DATA_UPDATE ( struct cli_hashtable * s , size_t tries )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . update + + ;
2007-03-11 11:14:35 +00:00
s - > PROFILE_STRUCT . update_tries + = tries ;
2006-12-26 16:17:02 +00:00
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_HASH_DELETE ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . deletes + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_HASH_CLEAR ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
s - > PROFILE_STRUCT . clear + + ;
}
2009-08-04 23:17:28 +02:00
static inline void PROFILE_REPORT ( const struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
size_t lookups , queries , insert_tries , inserts ;
2007-03-11 11:14:35 +00:00
cli_dbgmsg ( " --------Hashtable usage report for %p-------------- \n " , ( const void * ) s ) ;
2006-12-26 16:17:02 +00:00
cli_dbgmsg ( " hash function calculations:%ld \n " , s - > PROFILE_STRUCT . calc_hash ) ;
cli_dbgmsg ( " successfull finds/total searches: %ld/%ld; lookups: %ld \n " , s - > PROFILE_STRUCT . found , s - > PROFILE_STRUCT . find_req , s - > PROFILE_STRUCT . found_tries ) ;
cli_dbgmsg ( " unsuccessfull finds/total searches: %ld/%ld; lookups: %ld \n " , s - > PROFILE_STRUCT . not_found , s - > PROFILE_STRUCT . find_req , s - > PROFILE_STRUCT . not_found_tries ) ;
cli_dbgmsg ( " successfull finds during grow:%ld; lookups: %ld \n " , s - > PROFILE_STRUCT . grow_found , s - > PROFILE_STRUCT . grow_found_tries ) ;
lookups = s - > PROFILE_STRUCT . found_tries + s - > PROFILE_STRUCT . not_found_tries + s - > PROFILE_STRUCT . grow_found_tries ;
queries = s - > PROFILE_STRUCT . find_req + s - > PROFILE_STRUCT . grow_found ;
cli_dbgmsg ( " Find Lookups/total queries: %ld/%ld = %3f \n " , lookups , queries , lookups * 1.0 / queries ) ;
insert_tries = s - > PROFILE_STRUCT . insert_tries + s - > PROFILE_STRUCT . update_tries + s - > PROFILE_STRUCT . deleted_tries ;
cli_dbgmsg ( " new item insert tries/new items: %ld/%ld \n " , s - > PROFILE_STRUCT . insert_tries , s - > PROFILE_STRUCT . inserts ) ;
cli_dbgmsg ( " update tries/updates: %ld/%ld \n " , s - > PROFILE_STRUCT . update_tries , s - > PROFILE_STRUCT . update ) ;
cli_dbgmsg ( " deleted item reuse tries/deleted&reused items: %ld/%ld \n " , s - > PROFILE_STRUCT . deleted_tries , s - > PROFILE_STRUCT . deleted_reuse ) ;
inserts = s - > PROFILE_STRUCT . inserts + s - > PROFILE_STRUCT . update + s - > PROFILE_STRUCT . deleted_reuse ;
cli_dbgmsg ( " Insert tries/total inserts: %ld/%ld = %3f \n " , insert_tries , inserts , insert_tries * 1.0 / inserts ) ;
cli_dbgmsg ( " Grows: %ld, Deletes : %ld, hashtable clears: %ld \n " , s - > PROFILE_STRUCT . grow , s - > PROFILE_STRUCT . deletes , s - > PROFILE_STRUCT . clear ) ;
cli_dbgmsg ( " --------Report end------------- \n " ) ;
}
# else
# define PROFILE_INIT(s)
# define PROFILE_CALC_HASH(s)
# define PROFILE_FIND_ELEMENT(s)
# define PROFILE_FIND_NOTFOUND(s, tries)
# define PROFILE_FIND_FOUND(s, tries)
# define PROFILE_HASH_EXHAUSTED(s)
# define PROFILE_GROW_START(s)
# define PROFILE_GROW_FOUND(s, tries)
# define PROFILE_GROW_DONE(s)
# define PROFILE_DELETED_REUSE(s, tries)
# define PROFILE_INSERT(s, tries)
# define PROFILE_DATA_UPDATE(s, tries)
# define PROFILE_HASH_DELETE(s)
# define PROFILE_HASH_CLEAR(s)
# define PROFILE_REPORT(s)
# endif
2009-08-04 23:17:28 +02:00
int cli_hashtab_init ( struct cli_hashtable * s , size_t capacity )
2006-12-26 16:17:02 +00:00
{
if ( ! s )
return CL_ENULLARG ;
PROFILE_INIT ( s ) ;
2008-02-06 18:53:23 +00:00
capacity = nearest_power ( capacity ) ;
2006-12-26 16:17:02 +00:00
s - > htable = cli_calloc ( capacity , sizeof ( * s - > htable ) ) ;
if ( ! s - > htable )
return CL_EMEM ;
s - > capacity = capacity ;
s - > used = 0 ;
s - > maxfill = 8 * capacity / 10 ;
return 0 ;
}
2008-02-06 18:53:23 +00:00
static inline uint32_t hash32shift ( uint32_t key )
{
key = ~ key + ( key < < 15 ) ;
key = key ^ ( key > > 12 ) ;
key = key + ( key < < 2 ) ;
key = key ^ ( key > > 4 ) ;
key = ( key + ( key < < 3 ) ) + ( key < < 11 ) ;
key = key ^ ( key > > 16 ) ;
return key ;
}
static inline size_t hash ( const unsigned char * k , const size_t len , const size_t SIZE )
2006-12-26 16:17:02 +00:00
{
2008-02-06 18:53:23 +00:00
size_t Hash = 1 ;
2006-12-26 16:17:02 +00:00
size_t i ;
2008-02-06 18:53:23 +00:00
for ( i = 0 ; i < len ; i + + ) {
/* a simple add is good, because we use the mixing function below */
Hash + = k [ i ] ;
/* mixing function */
Hash = hash32shift ( Hash ) ;
}
/* SIZE is power of 2 */
return Hash & ( SIZE - 1 ) ;
2006-12-26 16:17:02 +00:00
}
/* if returned element has key==NULL, then key was not found in table */
2009-08-04 23:17:28 +02:00
struct cli_element * cli_hashtab_find ( const struct cli_hashtable * s , const char * key , const size_t len )
2006-12-26 16:17:02 +00:00
{
2009-08-04 23:17:28 +02:00
struct cli_element * element ;
2008-01-21 15:52:21 +00:00
size_t tries = 1 ;
2006-12-26 16:17:02 +00:00
size_t idx ;
if ( ! s )
2008-01-21 15:52:21 +00:00
return NULL ;
2006-12-26 16:17:02 +00:00
PROFILE_CALC_HASH ( s ) ;
PROFILE_FIND_ELEMENT ( s ) ;
2008-01-21 15:52:21 +00:00
idx = hash ( ( const unsigned char * ) key , len , s - > capacity ) ;
2006-12-26 16:17:02 +00:00
element = & s - > htable [ idx ] ;
do {
if ( ! element - > key ) {
PROFILE_FIND_NOTFOUND ( s , tries ) ;
return NULL ; /* element not found, place is empty*/
}
2008-06-17 16:44:55 +00:00
else if ( element - > key ! = DELETED_KEY & & len = = element - > len & & ( key = = element - > key | | strncmp ( key , element - > key , len ) = = 0 ) ) {
2006-12-26 16:17:02 +00:00
PROFILE_FIND_FOUND ( s , tries ) ;
return element ; /* found */
}
else {
2008-06-17 16:44:55 +00:00
idx = ( idx + tries + + ) & ( s - > capacity - 1 ) ;
2006-12-26 16:17:02 +00:00
element = & s - > htable [ idx ] ;
}
} while ( tries < = s - > capacity ) ;
PROFILE_HASH_EXHAUSTED ( s ) ;
return NULL ; /* not found */
}
2009-08-04 23:17:28 +02:00
static int cli_hashtab_grow ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
2008-06-16 21:21:11 +00:00
const size_t new_capacity = nearest_power ( s - > capacity + 1 ) ;
2009-08-04 23:17:28 +02:00
struct cli_element * htable = cli_calloc ( new_capacity , sizeof ( * s - > htable ) ) ;
2006-12-26 16:17:02 +00:00
size_t i , idx , used = 0 ;
2008-06-16 21:21:11 +00:00
cli_dbgmsg ( " hashtab.c: new capacity: %lu \n " , new_capacity ) ;
2006-12-26 16:17:02 +00:00
if ( new_capacity = = s - > capacity | | ! htable )
return CL_EMEM ;
PROFILE_GROW_START ( s ) ;
cli_dbgmsg ( " hashtab.c: Warning: growing open-addressing hashtables is slow. Either allocate more storage when initializing, or use other hashtable types! \n " ) ;
for ( i = 0 ; i < s - > capacity ; i + + ) {
if ( s - > htable [ i ] . key & & s - > htable [ i ] . key ! = DELETED_KEY ) {
2009-08-04 23:17:28 +02:00
struct cli_element * element ;
2008-01-21 15:52:21 +00:00
size_t tries = 1 ;
2006-12-26 16:17:02 +00:00
PROFILE_CALC_HASH ( s ) ;
2008-01-23 20:19:14 +00:00
idx = hash ( ( const unsigned char * ) s - > htable [ i ] . key , s - > htable [ i ] . len , new_capacity ) ;
2006-12-26 16:17:02 +00:00
element = & htable [ idx ] ;
while ( element - > key & & tries < = new_capacity ) {
idx = ( idx + tries + + ) % new_capacity ;
element = & htable [ idx ] ;
}
if ( ! element - > key ) {
/* copy element from old hashtable to new */
PROFILE_GROW_FOUND ( s , tries ) ;
* element = s - > htable [ i ] ;
used + + ;
}
else {
cli_errmsg ( " hashtab.c: Impossible - unable to rehash table " ) ;
return CL_EMEM ; /* this means we didn't find enough room for all elements in the new table, should never happen */
}
}
}
free ( s - > htable ) ;
s - > htable = htable ;
s - > used = used ;
s - > capacity = new_capacity ;
s - > maxfill = new_capacity * 8 / 10 ;
2007-03-11 11:14:35 +00:00
cli_dbgmsg ( " Table %p size after grow:%ld \n " , ( void * ) s , s - > capacity ) ;
2006-12-26 16:17:02 +00:00
PROFILE_GROW_DONE ( s ) ;
return CL_SUCCESS ;
}
2009-08-04 23:17:28 +02:00
const struct cli_element * cli_hashtab_insert ( struct cli_hashtable * s , const char * key , const size_t len , const cli_element_data data )
2006-12-26 16:17:02 +00:00
{
2009-08-04 23:17:28 +02:00
struct cli_element * element ;
struct cli_element * deleted_element = NULL ;
2008-01-21 15:52:21 +00:00
size_t tries = 1 ;
2006-12-26 16:17:02 +00:00
size_t idx ;
if ( ! s )
2008-06-16 21:21:11 +00:00
return NULL ;
if ( s - > used > s - > maxfill ) {
cli_dbgmsg ( " hashtab.c:Growing hashtable %p, because it has exceeded maxfill, old size:%ld \n " , ( void * ) s , s - > capacity ) ;
2009-08-04 23:17:28 +02:00
cli_hashtab_grow ( s ) ;
2008-06-16 21:21:11 +00:00
}
2006-12-26 16:17:02 +00:00
do {
PROFILE_CALC_HASH ( s ) ;
2008-01-21 15:52:21 +00:00
idx = hash ( ( const unsigned char * ) key , len , s - > capacity ) ;
2006-12-26 16:17:02 +00:00
element = & s - > htable [ idx ] ;
do {
if ( ! element - > key ) {
2008-01-21 15:52:21 +00:00
char * thekey ;
2006-12-26 16:17:02 +00:00
/* element not found, place is empty, insert*/
if ( deleted_element ) {
/* reuse deleted elements*/
element = deleted_element ;
PROFILE_DELETED_REUSE ( s , tries ) ;
}
else {
PROFILE_INSERT ( s , tries ) ;
}
thekey = cli_malloc ( len + 1 ) ;
if ( ! thekey )
2008-06-16 21:21:11 +00:00
return NULL ;
2008-01-23 20:19:14 +00:00
strncpy ( thekey , key , len + 1 ) ;
2008-05-27 16:30:47 +00:00
thekey [ len ] = ' \0 ' ;
2006-12-26 16:17:02 +00:00
element - > key = thekey ;
element - > data = data ;
2008-01-23 20:19:14 +00:00
element - > len = len ;
2008-01-21 15:52:21 +00:00
s - > used + + ;
2008-06-16 21:21:11 +00:00
return element ;
2006-12-26 16:17:02 +00:00
}
else if ( element - > key = = DELETED_KEY ) {
deleted_element = element ;
2010-08-10 15:59:57 +03:00
element - > key = NULL ;
2006-12-26 16:17:02 +00:00
}
2008-01-23 20:19:14 +00:00
else if ( len = = element - > len & & strncmp ( key , element - > key , len ) = = 0 ) {
2006-12-26 16:17:02 +00:00
PROFILE_DATA_UPDATE ( s , tries ) ;
element - > data = data ; /* key found, update */
2008-06-16 21:21:11 +00:00
return element ;
2006-12-26 16:17:02 +00:00
}
else {
idx = ( idx + tries + + ) % s - > capacity ;
element = & s - > htable [ idx ] ;
}
} while ( tries < = s - > capacity ) ;
/* no free place found*/
PROFILE_HASH_EXHAUSTED ( s ) ;
2007-03-11 11:14:35 +00:00
cli_dbgmsg ( " hashtab.c: Growing hashtable %p, because its full, old size:%ld. \n " , ( void * ) s , s - > capacity ) ;
2009-08-04 23:17:28 +02:00
} while ( cli_hashtab_grow ( s ) > = 0 ) ;
2006-12-26 16:17:02 +00:00
cli_warnmsg ( " hashtab.c: Unable to grow hashtable \n " ) ;
2008-06-16 21:21:11 +00:00
return NULL ;
2006-12-26 16:17:02 +00:00
}
2010-05-12 18:26:02 +03:00
void cli_hashtab_delete ( struct cli_hashtable * s , const char * key , const size_t len )
{
struct cli_element * el = cli_hashtab_find ( s , key , len ) ;
2010-05-13 23:35:47 +03:00
if ( ! el | | el - > key = = DELETED_KEY )
2010-05-12 18:26:02 +03:00
return ;
2010-05-14 10:40:04 +03:00
free ( ( void * ) el - > key ) ;
2010-05-12 18:26:02 +03:00
el - > key = DELETED_KEY ;
}
2009-08-04 23:17:28 +02:00
void cli_hashtab_clear ( struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
size_t i ;
PROFILE_HASH_CLEAR ( s ) ;
for ( i = 0 ; i < s - > capacity ; i + + ) {
if ( s - > htable [ i ] . key & & s - > htable [ i ] . key ! = DELETED_KEY )
2008-01-21 14:29:49 +00:00
free ( ( void * ) s - > htable [ i ] . key ) ;
2006-12-26 16:17:02 +00:00
}
2008-07-23 13:51:57 +00:00
if ( s - > htable )
memset ( s - > htable , 0 , s - > capacity ) ;
2006-12-26 16:17:02 +00:00
s - > used = 0 ;
}
2009-08-04 23:17:28 +02:00
void cli_hashtab_free ( struct cli_hashtable * s )
2008-07-23 13:51:57 +00:00
{
2009-08-04 23:17:28 +02:00
cli_hashtab_clear ( s ) ;
2008-07-23 13:51:57 +00:00
free ( s - > htable ) ;
s - > htable = NULL ;
s - > capacity = 0 ;
}
2006-12-26 16:17:02 +00:00
2009-08-04 23:17:28 +02:00
int cli_hashtab_store ( const struct cli_hashtable * s , FILE * out )
2006-12-26 16:17:02 +00:00
{
size_t i ;
for ( i = 0 ; i < s - > capacity ; i + + ) {
2009-08-04 23:17:28 +02:00
const struct cli_element * e = & s - > htable [ i ] ;
2006-12-26 16:17:02 +00:00
if ( e - > key & & e - > key ! = DELETED_KEY ) {
fprintf ( out , " %ld %s \n " , e - > data , e - > key ) ;
}
}
return CL_SUCCESS ;
}
2009-08-04 23:17:28 +02:00
int cli_hashtab_generate_c ( const struct cli_hashtable * s , const char * name )
2006-12-26 16:17:02 +00:00
{
size_t i ;
printf ( " /* TODO: include GPL headers */ \n " ) ;
printf ( " #include <hashtab.h> \n " ) ;
2009-08-04 23:17:28 +02:00
printf ( " static struct cli_element %s_elements[] = { \n " , name ) ;
2006-12-26 16:17:02 +00:00
for ( i = 0 ; i < s - > capacity ; i + + ) {
2009-08-04 23:17:28 +02:00
const struct cli_element * e = & s - > htable [ i ] ;
2006-12-26 16:17:02 +00:00
if ( ! e - > key )
2008-01-23 20:19:14 +00:00
printf ( " \t {NULL,0,0}, \n " ) ;
2006-12-26 16:17:02 +00:00
else if ( e - > key = = DELETED_KEY )
2008-01-23 20:19:14 +00:00
printf ( " \t {DELETED_KEY,0,0}, \n " ) ;
2006-12-26 16:17:02 +00:00
else
2008-01-23 20:19:14 +00:00
printf ( " \t { \" %s \" , %ld, %ld}, \n " , e - > key , e - > data , e - > len ) ;
2006-12-26 16:17:02 +00:00
}
printf ( " }; \n " ) ;
2009-08-04 23:17:28 +02:00
printf ( " const struct cli_hashtable %s = { \n " , name ) ;
2006-12-26 16:17:02 +00:00
printf ( " \t %s_elements, %ld, %ld, %ld " , name , s - > capacity , s - > used , s - > maxfill ) ;
printf ( " \n }; \n " ) ;
PROFILE_REPORT ( s ) ;
return 0 ;
}
2009-08-04 23:17:28 +02:00
int cli_hashtab_load ( FILE * in , struct cli_hashtable * s )
2006-12-26 16:17:02 +00:00
{
char line [ 1024 ] ;
while ( fgets ( line , sizeof ( line ) , in ) ) {
2008-01-21 15:52:21 +00:00
char l [ 1024 ] ;
2006-12-26 16:17:02 +00:00
int val ;
sscanf ( line , " %d %1023s " , & val , l ) ;
2009-08-04 23:17:28 +02:00
cli_hashtab_insert ( s , l , strlen ( l ) , val ) ;
2006-12-26 16:17:02 +00:00
}
return CL_SUCCESS ;
}
2008-02-06 18:53:23 +00:00
/* Initialize hashset. @initial_capacity is rounded to nearest power of 2.
* Load factor is between 50 and 99. When capacity * load_factor / 100 is reached , the hashset is growed */
2009-08-04 23:17:28 +02:00
int cli_hashset_init ( struct cli_hashset * hs , size_t initial_capacity , uint8_t load_factor )
2008-02-06 18:53:23 +00:00
{
if ( load_factor < 50 | | load_factor > 99 ) {
cli_dbgmsg ( MODULE_NAME " Invalid load factor: %u, using default of 80%% \n " , load_factor ) ;
load_factor = 80 ;
}
initial_capacity = nearest_power ( initial_capacity ) ;
hs - > limit = initial_capacity * load_factor / 100 ;
hs - > capacity = initial_capacity ;
hs - > mask = initial_capacity - 1 ;
hs - > count = 0 ;
hs - > keys = cli_malloc ( initial_capacity * sizeof ( * hs - > keys ) ) ;
if ( ! hs - > keys ) {
return CL_EMEM ;
}
2010-01-04 17:17:22 +02:00
hs - > bitmap = cli_calloc ( initial_capacity > > 5 , sizeof ( * hs - > bitmap ) ) ;
2008-02-06 18:53:23 +00:00
if ( ! hs - > bitmap ) {
free ( hs - > keys ) ;
return CL_EMEM ;
}
return 0 ;
}
2009-08-04 23:17:28 +02:00
void cli_hashset_destroy ( struct cli_hashset * hs )
2008-02-06 18:53:23 +00:00
{
2009-02-19 08:50:04 +00:00
cli_dbgmsg ( MODULE_NAME " Freeing hashset, elements: %u, capacity: %u \n " , hs - > count , hs - > capacity ) ;
2008-02-06 18:53:23 +00:00
free ( hs - > keys ) ;
free ( hs - > bitmap ) ;
hs - > keys = hs - > bitmap = NULL ;
hs - > capacity = 0 ;
}
# define BITMAP_CONTAINS(bmap, val) ((bmap)[(val) >> 5] & (1 << ((val) & 0x1f)))
# define BITMAP_INSERT(bmap, val) ((bmap)[(val) >> 5] |= (1 << ((val) & 0x1f)))
2010-03-24 17:59:41 +02:00
# define BITMAP_REMOVE(bmap, val) ((bmap)[(val) >> 5] &= ~(1 << ((val) & 0x1f)))
2008-02-06 18:53:23 +00:00
/*
* searches the hashset for the @ key .
* Returns the position the key is at , or a candidate position where it could be inserted .
*/
2009-08-04 23:17:28 +02:00
static inline size_t cli_hashset_search ( const struct cli_hashset * hs , const uint32_t key )
2008-02-06 18:53:23 +00:00
{
/* calculate hash value for this key, and map it to our table */
size_t idx = hash32shift ( key ) & ( hs - > mask ) ;
size_t tries = 1 ;
/* check wether the entry is used, and if the key matches */
while ( BITMAP_CONTAINS ( hs - > bitmap , idx ) & & ( hs - > keys [ idx ] ! = key ) ) {
/* entry used, key different -> collision */
idx = ( idx + tries + + ) & ( hs - > mask ) ;
/* quadratic probing, with c1 = c2 = 1/2, guaranteed to walk the entire table
* for table sizes power of 2. */
}
/* we have either found the key, or a candidate insertion position */
return idx ;
}
2009-08-04 23:17:28 +02:00
static void cli_hashset_addkey_internal ( struct cli_hashset * hs , const uint32_t key )
2008-02-06 18:53:23 +00:00
{
2009-08-04 23:17:28 +02:00
const size_t idx = cli_hashset_search ( hs , key ) ;
2008-02-06 18:53:23 +00:00
/* we know hashtable is not full, when this method is called */
if ( ! BITMAP_CONTAINS ( hs - > bitmap , idx ) ) {
/* add new key */
BITMAP_INSERT ( hs - > bitmap , idx ) ;
hs - > keys [ idx ] = key ;
hs - > count + + ;
}
}
2009-08-04 23:17:28 +02:00
static int cli_hashset_grow ( struct cli_hashset * hs )
2008-02-06 18:53:23 +00:00
{
2009-08-04 23:17:28 +02:00
struct cli_hashset new_hs ;
2008-02-06 18:53:23 +00:00
size_t i ;
int rc ;
/* in-place growing is not possible, since the new keys
* will hash to different locations . */
2009-02-19 08:50:04 +00:00
cli_dbgmsg ( MODULE_NAME " Growing hashset, used: %u, capacity: %u \n " , hs - > count , hs - > capacity ) ;
2008-02-06 18:53:23 +00:00
/* create a bigger hashset */
2009-08-04 23:17:28 +02:00
if ( ( rc = cli_hashset_init ( & new_hs , hs - > capacity < < 1 , hs - > limit * 100 / hs - > capacity ) ) < 0 ) {
2008-02-06 18:53:23 +00:00
return rc ;
}
/* and copy keys */
for ( i = 0 ; i < hs - > capacity ; i + + ) {
if ( BITMAP_CONTAINS ( hs - > bitmap , i ) ) {
const size_t key = hs - > keys [ i ] ;
2009-08-04 23:17:28 +02:00
cli_hashset_addkey_internal ( & new_hs , key ) ;
2008-02-06 18:53:23 +00:00
}
}
2009-08-04 23:17:28 +02:00
cli_hashset_destroy ( hs ) ;
2008-02-06 18:53:23 +00:00
/* replace old hashset with new one */
* hs = new_hs ;
return 0 ;
}
2009-08-04 23:17:28 +02:00
int cli_hashset_addkey ( struct cli_hashset * hs , const uint32_t key )
2008-02-06 18:53:23 +00:00
{
/* check that we didn't reach the load factor.
* Even if we don ' t know yet whether we ' d add this key */
if ( hs - > count + 1 > hs - > limit ) {
2009-08-04 23:17:28 +02:00
int rc = cli_hashset_grow ( hs ) ;
2008-02-06 18:53:23 +00:00
if ( rc ) {
return rc ;
}
}
2009-08-04 23:17:28 +02:00
cli_hashset_addkey_internal ( hs , key ) ;
2008-02-06 18:53:23 +00:00
return 0 ;
}
2010-03-24 17:59:41 +02:00
int cli_hashset_removekey ( struct cli_hashset * hs , const uint32_t key )
{
const size_t idx = cli_hashset_search ( hs , key ) ;
if ( BITMAP_CONTAINS ( hs - > bitmap , idx ) ) {
BITMAP_REMOVE ( hs - > bitmap , idx ) ;
hs - > keys [ idx ] = 0 ;
hs - > count - - ;
2010-03-24 18:16:50 +02:00
return 0 ;
2010-03-24 17:59:41 +02:00
}
2010-03-24 18:16:50 +02:00
return - 1 ;
2010-03-24 17:59:41 +02:00
}
2009-08-04 23:17:28 +02:00
int cli_hashset_contains ( const struct cli_hashset * hs , const uint32_t key )
2008-02-06 18:53:23 +00:00
{
2009-08-04 23:17:28 +02:00
const size_t idx = cli_hashset_search ( hs , key ) ;
2008-02-06 18:53:23 +00:00
return BITMAP_CONTAINS ( hs - > bitmap , idx ) ;
}
2009-08-04 23:17:28 +02:00
ssize_t cli_hashset_toarray ( const struct cli_hashset * hs , uint32_t * * array )
2008-02-06 18:53:23 +00:00
{
size_t i , j ;
uint32_t * arr ;
if ( ! array ) {
return CL_ENULLARG ;
}
* array = arr = cli_malloc ( hs - > count * sizeof ( * arr ) ) ;
if ( ! arr ) {
return CL_EMEM ;
}
for ( i = 0 , j = 0 ; i < hs - > capacity & & j < hs - > count ; i + + ) {
if ( BITMAP_CONTAINS ( hs - > bitmap , i ) ) {
arr [ j + + ] = hs - > keys [ i ] ;
}
}
return j ;
}
2010-01-04 17:08:59 +02:00
void cli_hashset_init_noalloc ( struct cli_hashset * hs )
{
2010-01-04 18:10:23 +01:00
memset ( hs , 0 , sizeof ( * hs ) ) ;
2010-01-04 17:08:59 +02:00
}
int cli_hashset_contains_maybe_noalloc ( const struct cli_hashset * hs , const uint32_t key )
{
if ( ! hs - > keys )
return 0 ;
return cli_hashset_contains ( hs , key ) ;
}
2010-05-12 18:26:02 +03:00
int cli_map_init ( struct cli_map * m , int32_t keysize , int32_t valuesize ,
int32_t capacity )
{
if ( keysize < = 0 | | valuesize < 0 | | capacity < = 0 )
return - CL_EARG ;
memset ( m , 0 , sizeof ( * m ) ) ;
cli_hashtab_init ( & m - > htab , 16 ) ;
m - > keysize = keysize ;
m - > valuesize = valuesize ;
m - > last_insert = - 1 ;
m - > last_find = - 1 ;
return 0 ;
}
int cli_map_addkey ( struct cli_map * m , const void * key , int32_t keysize )
{
unsigned n ;
struct cli_element * el ;
if ( m - > keysize ! = keysize )
return - CL_EARG ;
el = cli_hashtab_find ( & m - > htab , key , keysize ) ;
if ( el ) {
m - > last_insert = el - > data ;
return 0 ;
}
n = m - > nvalues + 1 ;
if ( m - > valuesize ) {
void * v ;
v = cli_realloc ( m - > u . sized_values , n * m - > valuesize ) ;
if ( ! v )
return - CL_EMEM ;
m - > u . sized_values = v ;
memset ( ( char * ) m - > u . sized_values + ( n - 1 ) * m - > valuesize , 0 , m - > valuesize ) ;
} else {
struct cli_map_value * v ;
v = cli_realloc ( m - > u . unsized_values , n * sizeof ( * m - > u . unsized_values ) ) ;
if ( ! v )
return - CL_EMEM ;
m - > u . unsized_values = v ;
memset ( & m - > u . unsized_values [ n - 1 ] , 0 , sizeof ( * m - > u . unsized_values ) ) ;
}
m - > nvalues = n ;
if ( ! cli_hashtab_insert ( & m - > htab , key , keysize , n - 1 ) )
return - CL_EMEM ;
m - > last_insert = n - 1 ;
return 1 ;
}
int cli_map_removekey ( struct cli_map * m , const void * key , int32_t keysize )
{
struct cli_element * el ;
if ( m - > keysize ! = keysize )
return - CL_EARG ;
el = cli_hashtab_find ( & m - > htab , key , keysize ) ;
if ( ! el )
return 0 ;
if ( el - > data > = m - > nvalues | | el - > data < 0 )
return - CL_EARG ;
if ( ! m - > valuesize ) {
struct cli_map_value * v = & m - > u . unsized_values [ el - > data ] ;
free ( v - > value ) ;
v - > value = NULL ;
v - > valuesize = 0 ;
} else {
char * v = ( char * ) m - > u . sized_values + el - > data * m - > valuesize ;
memset ( v , 0 , m - > valuesize ) ;
}
cli_hashtab_delete ( & m - > htab , key , keysize ) ;
return 1 ;
}
int cli_map_setvalue ( struct cli_map * m , const void * value , int32_t valuesize )
{
if ( ( m - > valuesize & & m - > valuesize ! = valuesize )
| | m - > last_insert > = m - > nvalues | | m - > last_insert < 0 )
return - CL_EARG ;
if ( m - > valuesize ) {
memcpy ( ( char * ) m - > u . sized_values + m - > last_insert * m - > valuesize ,
value , valuesize ) ;
} else {
struct cli_map_value * v = & m - > u . unsized_values [ m - > last_insert ] ;
if ( v - > value )
free ( v - > value ) ;
v - > value = cli_malloc ( valuesize ) ;
if ( ! v - > value )
return - CL_EMEM ;
memcpy ( v - > value , value , valuesize ) ;
v - > valuesize = valuesize ;
}
return 0 ;
}
int cli_map_find ( struct cli_map * m , const void * key , int32_t keysize )
{
struct cli_element * el ;
if ( m - > keysize ! = keysize )
return - CL_EARG ;
el = cli_hashtab_find ( & m - > htab , key , keysize ) ;
if ( ! el )
return 0 ;
m - > last_find = el - > data ;
return 1 ;
}
int cli_map_getvalue_size ( struct cli_map * m )
{
if ( m - > valuesize )
return m - > valuesize ;
if ( m - > last_find < 0 | | m - > last_find > = m - > nvalues )
return - CL_EARG ;
return m - > u . unsized_values [ m - > last_find ] . valuesize ;
}
void * cli_map_getvalue ( struct cli_map * m )
{
if ( m - > last_find < 0 | | m - > last_find > = m - > nvalues )
return NULL ;
if ( m - > valuesize )
return ( char * ) m - > u . sized_values + m - > last_find * m - > valuesize ;
2010-05-13 20:37:06 +03:00
return m - > u . unsized_values [ m - > last_find ] . value ;
2010-05-12 18:26:02 +03:00
}
void cli_map_delete ( struct cli_map * m )
{
cli_hashtab_free ( & m - > htab ) ;
if ( ! m - > valuesize ) {
unsigned i ;
for ( i = 0 ; i < m - > nvalues ; i + + )
free ( m - > u . unsized_values [ i ] . value ) ;
2010-05-13 23:35:47 +03:00
free ( m - > u . unsized_values ) ;
} else {
free ( m - > u . sized_values ) ;
2010-05-12 18:26:02 +03:00
}
memset ( m , 0 , sizeof ( * m ) ) ;
}