2018-05-14 14:16:43 +03:00
/*
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
* Copyright ( C ) 2004 - 2011 Red Hat , Inc . All rights reserved .
*
* This file is part of the device - mapper userspace tools .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser 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
*/
2018-06-04 15:22:14 +03:00
# include "device_mapper/misc/dmlib.h"
2018-06-08 15:40:53 +03:00
# include "base/memory/zalloc.h"
2018-06-08 15:54:19 +03:00
# include "hash.h"
2018-05-14 14:16:43 +03:00
struct dm_hash_node {
struct dm_hash_node * next ;
void * data ;
unsigned data_len ;
unsigned keylen ;
2021-03-07 17:33:04 +03:00
unsigned hash ;
2021-09-22 18:05:09 +03:00
char key [ 0 ] ;
2018-05-14 14:16:43 +03:00
} ;
struct dm_hash_table {
unsigned num_nodes ;
2021-03-07 17:33:04 +03:00
unsigned num_hint ;
unsigned mask_slots ; /* (slots - 1) -> used as hash mask */
unsigned collisions ; /* Collissions of hash keys */
unsigned search ; /* How many keys were searched */
unsigned found ; /* How many nodes were found */
unsigned same_hash ; /* Was there a colision with same masked hash and len ? */
2018-05-14 14:16:43 +03:00
struct dm_hash_node * * slots ;
} ;
2021-03-08 17:18:04 +03:00
#if 0 /* TO BE REMOVED */
static unsigned _hash ( const void * key , unsigned len )
{
/* Permutation of the Integers 0 through 255 */
static unsigned char _nums [ ] = {
2018-05-14 14:16:43 +03:00
1 , 14 , 110 , 25 , 97 , 174 , 132 , 119 , 138 , 170 , 125 , 118 , 27 , 233 , 140 , 51 ,
87 , 197 , 177 , 107 , 234 , 169 , 56 , 68 , 30 , 7 , 173 , 73 , 188 , 40 , 36 , 65 ,
49 , 213 , 104 , 190 , 57 , 211 , 148 , 223 , 48 , 115 , 15 , 2 , 67 , 186 , 210 , 28 ,
12 , 181 , 103 , 70 , 22 , 58 , 75 , 78 , 183 , 167 , 238 , 157 , 124 , 147 , 172 ,
144 ,
176 , 161 , 141 , 86 , 60 , 66 , 128 , 83 , 156 , 241 , 79 , 46 , 168 , 198 , 41 , 254 ,
178 , 85 , 253 , 237 , 250 , 154 , 133 , 88 , 35 , 206 , 95 , 116 , 252 , 192 , 54 ,
221 ,
102 , 218 , 255 , 240 , 82 , 106 , 158 , 201 , 61 , 3 , 89 , 9 , 42 , 155 , 159 , 93 ,
166 , 80 , 50 , 34 , 175 , 195 , 100 , 99 , 26 , 150 , 16 , 145 , 4 , 33 , 8 , 189 ,
121 , 64 , 77 , 72 , 208 , 245 , 130 , 122 , 143 , 55 , 105 , 134 , 29 , 164 , 185 ,
194 ,
193 , 239 , 101 , 242 , 5 , 171 , 126 , 11 , 74 , 59 , 137 , 228 , 108 , 191 , 232 ,
139 ,
6 , 24 , 81 , 20 , 127 , 17 , 91 , 92 , 251 , 151 , 225 , 207 , 21 , 98 , 113 , 112 ,
84 , 226 , 18 , 214 , 199 , 187 , 13 , 32 , 94 , 220 , 224 , 212 , 247 , 204 , 196 ,
43 ,
249 , 236 , 45 , 244 , 111 , 182 , 153 , 136 , 129 , 90 , 217 , 202 , 19 , 165 , 231 ,
71 ,
230 , 142 , 96 , 227 , 62 , 179 , 246 , 114 , 162 , 53 , 160 , 215 , 205 , 180 , 47 ,
109 ,
44 , 38 , 31 , 149 , 135 , 0 , 216 , 52 , 63 , 23 , 37 , 69 , 39 , 117 , 146 , 184 ,
163 , 200 , 222 , 235 , 248 , 243 , 219 , 10 , 152 , 131 , 123 , 229 , 203 , 76 , 120 ,
209
2021-03-08 17:18:04 +03:00
} ;
2018-05-14 14:16:43 +03:00
2021-03-08 17:18:04 +03:00
const uint8_t * str = key ;
2021-03-07 04:13:02 +03:00
unsigned h = 0 , g ;
2018-05-14 14:16:43 +03:00
unsigned i ;
for ( i = 0 ; i < len ; i + + ) {
h < < = 4 ;
2020-08-29 23:37:17 +03:00
h + = _nums [ * str + + ] ;
2021-03-07 04:13:02 +03:00
g = h & ( ( unsigned ) 0xf < < 16u ) ;
2018-05-14 14:16:43 +03:00
if ( g ) {
h ^ = g > > 16u ;
h ^ = g > > 5u ;
}
}
return h ;
}
2021-03-08 17:18:04 +03:00
/* In-kernel DM hashing, still lots of collisions */
static unsigned _hash_in_kernel ( const char * key , unsigned len )
{
const unsigned char * str = ( unsigned char * ) key ;
const unsigned hash_mult = 2654435387U ;
unsigned hash = 0 , i ;
for ( i = 0 ; i < len ; + + i )
hash = ( hash + str [ i ] ) * hash_mult ;
return hash ;
}
# endif
# undef get16bits
# if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
# define get16bits(d) (*((const uint16_t *) (d)))
# endif
# if !defined (get16bits)
# define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ ( uint32_t ) ( ( ( const uint8_t * ) ( d ) ) [ 0 ] ) )
# endif
/*
* Adapted Bob Jenkins hash to read by 2 bytes if possible .
* https : //secure.wikimedia.org/wikipedia/en/wiki/Jenkins_hash_function
*
* Reduces amount of hash collisions
*/
static unsigned _hash ( const void * key , unsigned len )
{
const uint8_t * str = ( uint8_t * ) key ;
unsigned hash = 0 , i ;
unsigned sz = len / 2 ;
for ( i = 0 ; i < sz ; + + i ) {
hash + = get16bits ( str + 2 * i ) ;
hash + = ( hash < < 10 ) ;
hash ^ = ( hash > > 6 ) ;
}
if ( len & 1 ) {
hash + = str [ len - 1 ] ;
hash + = ( hash < < 10 ) ;
hash ^ = ( hash > > 6 ) ;
}
hash + = ( hash < < 3 ) ;
hash ^ = ( hash > > 11 ) ;
hash + = ( hash < < 15 ) ;
return hash ;
}
static struct dm_hash_node * _create_node ( const void * key , unsigned len )
{
struct dm_hash_node * n = malloc ( sizeof ( * n ) + len ) ;
if ( n ) {
memcpy ( n - > key , key , len ) ;
n - > keylen = len ;
}
return n ;
}
2018-05-14 14:16:43 +03:00
struct dm_hash_table * dm_hash_create ( unsigned size_hint )
{
size_t len ;
unsigned new_size = 16u ;
2018-06-08 15:40:53 +03:00
struct dm_hash_table * hc = zalloc ( sizeof ( * hc ) ) ;
2018-05-14 14:16:43 +03:00
2021-03-07 17:33:04 +03:00
if ( ! hc ) {
log_error ( " Failed to allocate memory for hash. " ) ;
return 0 ;
}
hc - > num_hint = size_hint ;
2018-05-14 14:16:43 +03:00
/* round size hint up to a power of two */
while ( new_size < size_hint )
new_size = new_size < < 1 ;
2021-03-07 17:33:04 +03:00
hc - > mask_slots = new_size - 1 ;
2018-05-14 14:16:43 +03:00
len = sizeof ( * ( hc - > slots ) ) * new_size ;
2021-03-07 17:33:04 +03:00
if ( ! ( hc - > slots = zalloc ( len ) ) ) {
free ( hc ) ;
log_error ( " Failed to allocate slots for hash. " ) ;
return 0 ;
}
2018-05-14 14:16:43 +03:00
return hc ;
}
static void _free_nodes ( struct dm_hash_table * t )
{
struct dm_hash_node * c , * n ;
unsigned i ;
2021-03-07 17:33:04 +03:00
# ifdef DEBUG
log_debug ( " Free hash hint:%d slots:%d nodes:%d (s:%d f:%d c:%d h:%d) " ,
t - > num_hint , t - > mask_slots + 1 , t - > num_nodes ,
t - > search , t - > found , t - > collisions , t - > same_hash ) ;
# endif
if ( ! t - > num_nodes )
return ;
for ( i = 0 ; i < = t - > mask_slots ; i + + )
2018-05-14 14:16:43 +03:00
for ( c = t - > slots [ i ] ; c ; c = n ) {
n = c - > next ;
2018-06-08 15:40:53 +03:00
free ( c ) ;
2018-05-14 14:16:43 +03:00
}
}
void dm_hash_destroy ( struct dm_hash_table * t )
{
_free_nodes ( t ) ;
2018-06-08 15:40:53 +03:00
free ( t - > slots ) ;
free ( t ) ;
2018-05-14 14:16:43 +03:00
}
2021-03-07 17:33:04 +03:00
static struct dm_hash_node * * _findh ( struct dm_hash_table * t , const void * key ,
uint32_t len , unsigned hash )
2018-05-14 14:16:43 +03:00
{
struct dm_hash_node * * c ;
2021-03-07 17:33:04 +03:00
+ + t - > search ;
for ( c = & t - > slots [ hash & t - > mask_slots ] ; * c ; c = & ( ( * c ) - > next ) ) {
if ( ( * c ) - > keylen = = len & & ( * c ) - > hash = = hash ) {
if ( ! memcmp ( key , ( * c ) - > key , len ) ) {
+ + t - > found ;
break ;
}
+ + t - > same_hash ;
}
+ + t - > collisions ;
2018-05-14 14:16:43 +03:00
}
return c ;
}
2021-03-07 17:33:04 +03:00
static struct dm_hash_node * * _find ( struct dm_hash_table * t , const void * key ,
uint32_t len )
{
return _findh ( t , key , len , _hash ( key , len ) ) ;
}
2018-05-14 14:16:43 +03:00
void * dm_hash_lookup_binary ( struct dm_hash_table * t , const void * key ,
uint32_t len )
{
struct dm_hash_node * * c = _find ( t , key , len ) ;
return * c ? ( * c ) - > data : 0 ;
}
int dm_hash_insert_binary ( struct dm_hash_table * t , const void * key ,
uint32_t len , void * data )
{
2021-03-07 17:33:04 +03:00
unsigned hash = _hash ( key , len ) ;
struct dm_hash_node * * c = _findh ( t , key , len , hash ) ;
2018-05-14 14:16:43 +03:00
if ( * c )
( * c ) - > data = data ;
else {
struct dm_hash_node * n = _create_node ( key , len ) ;
if ( ! n )
return 0 ;
n - > data = data ;
2021-03-07 17:33:04 +03:00
n - > hash = hash ;
2018-05-14 14:16:43 +03:00
n - > next = 0 ;
* c = n ;
t - > num_nodes + + ;
}
return 1 ;
}
void dm_hash_remove_binary ( struct dm_hash_table * t , const void * key ,
uint32_t len )
{
struct dm_hash_node * * c = _find ( t , key , len ) ;
if ( * c ) {
struct dm_hash_node * old = * c ;
* c = ( * c ) - > next ;
2018-06-08 15:40:53 +03:00
free ( old ) ;
2018-05-14 14:16:43 +03:00
t - > num_nodes - - ;
}
}
void * dm_hash_lookup ( struct dm_hash_table * t , const char * key )
{
return dm_hash_lookup_binary ( t , key , strlen ( key ) + 1 ) ;
}
int dm_hash_insert ( struct dm_hash_table * t , const char * key , void * data )
{
return dm_hash_insert_binary ( t , key , strlen ( key ) + 1 , data ) ;
}
void dm_hash_remove ( struct dm_hash_table * t , const char * key )
{
dm_hash_remove_binary ( t , key , strlen ( key ) + 1 ) ;
}
static struct dm_hash_node * * _find_str_with_val ( struct dm_hash_table * t ,
const void * key , const void * val ,
uint32_t len , uint32_t val_len )
{
struct dm_hash_node * * c ;
unsigned h ;
2021-03-07 17:33:04 +03:00
h = _hash ( key , len ) & t - > mask_slots ;
2018-05-14 14:16:43 +03:00
for ( c = & t - > slots [ h ] ; * c ; c = & ( ( * c ) - > next ) ) {
if ( ( * c ) - > keylen ! = len )
continue ;
if ( ! memcmp ( key , ( * c ) - > key , len ) & & ( * c ) - > data ) {
if ( ( ( * c ) - > data_len = = val_len ) & &
! memcmp ( val , ( * c ) - > data , val_len ) )
return c ;
}
}
return NULL ;
}
int dm_hash_insert_allow_multiple ( struct dm_hash_table * t , const char * key ,
const void * val , uint32_t val_len )
{
struct dm_hash_node * n ;
struct dm_hash_node * first ;
int len = strlen ( key ) + 1 ;
unsigned h ;
n = _create_node ( key , len ) ;
if ( ! n )
return 0 ;
n - > data = ( void * ) val ;
n - > data_len = val_len ;
2021-03-07 17:33:04 +03:00
h = _hash ( key , len ) & t - > mask_slots ;
2018-05-14 14:16:43 +03:00
first = t - > slots [ h ] ;
if ( first )
n - > next = first ;
else
n - > next = 0 ;
t - > slots [ h ] = n ;
t - > num_nodes + + ;
return 1 ;
}
/*
* Look through multiple entries with the same key for one that has a
* matching val and return that . If none have maching val , return NULL .
*/
void * dm_hash_lookup_with_val ( struct dm_hash_table * t , const char * key ,
const void * val , uint32_t val_len )
{
struct dm_hash_node * * c ;
c = _find_str_with_val ( t , key , val , strlen ( key ) + 1 , val_len ) ;
return ( c & & * c ) ? ( * c ) - > data : 0 ;
}
/*
* Look through multiple entries with the same key for one that has a
* matching val and remove that .
*/
void dm_hash_remove_with_val ( struct dm_hash_table * t , const char * key ,
const void * val , uint32_t val_len )
{
struct dm_hash_node * * c ;
c = _find_str_with_val ( t , key , val , strlen ( key ) + 1 , val_len ) ;
if ( c & & * c ) {
struct dm_hash_node * old = * c ;
* c = ( * c ) - > next ;
2018-06-08 15:40:53 +03:00
free ( old ) ;
2018-05-14 14:16:43 +03:00
t - > num_nodes - - ;
}
}
/*
* Look up the value for a key and count how many
* entries have the same key .
*
* If no entries have key , return NULL and set count to 0.
*
* If one entry has the key , the function returns the val ,
* and sets count to 1.
*
* If N entries have the key , the function returns the val
* from the first entry , and sets count to N .
*/
void * dm_hash_lookup_with_count ( struct dm_hash_table * t , const char * key , int * count )
{
struct dm_hash_node * * c ;
struct dm_hash_node * * c1 = NULL ;
uint32_t len = strlen ( key ) + 1 ;
unsigned h ;
* count = 0 ;
2021-03-07 17:33:04 +03:00
h = _hash ( key , len ) & t - > mask_slots ;
2018-05-14 14:16:43 +03:00
for ( c = & t - > slots [ h ] ; * c ; c = & ( ( * c ) - > next ) ) {
if ( ( * c ) - > keylen ! = len )
continue ;
if ( ! memcmp ( key , ( * c ) - > key , len ) ) {
( * count ) + + ;
if ( ! c1 )
c1 = c ;
}
}
if ( ! c1 )
return NULL ;
else
return * c1 ? ( * c1 ) - > data : 0 ;
}
unsigned dm_hash_get_num_entries ( struct dm_hash_table * t )
{
return t - > num_nodes ;
}
void dm_hash_iter ( struct dm_hash_table * t , dm_hash_iterate_fn f )
{
struct dm_hash_node * c , * n ;
unsigned i ;
2021-03-07 17:33:04 +03:00
for ( i = 0 ; i < = t - > mask_slots ; i + + )
2018-05-14 14:16:43 +03:00
for ( c = t - > slots [ i ] ; c ; c = n ) {
n = c - > next ;
f ( c - > data ) ;
}
}
void dm_hash_wipe ( struct dm_hash_table * t )
{
_free_nodes ( t ) ;
2021-03-07 17:33:04 +03:00
memset ( t - > slots , 0 , sizeof ( struct dm_hash_node * ) * ( t - > mask_slots + 1 ) ) ;
t - > num_nodes = t - > collisions = t - > search = t - > same_hash = 0u ;
2018-05-14 14:16:43 +03:00
}
char * dm_hash_get_key ( struct dm_hash_table * t __attribute__ ( ( unused ) ) ,
struct dm_hash_node * n )
{
return n - > key ;
}
void * dm_hash_get_data ( struct dm_hash_table * t __attribute__ ( ( unused ) ) ,
struct dm_hash_node * n )
{
return n - > data ;
}
static struct dm_hash_node * _next_slot ( struct dm_hash_table * t , unsigned s )
{
struct dm_hash_node * c = NULL ;
unsigned i ;
2021-03-07 17:33:04 +03:00
for ( i = s ; i < = t - > mask_slots & & ! c ; i + + )
2018-05-14 14:16:43 +03:00
c = t - > slots [ i ] ;
return c ;
}
struct dm_hash_node * dm_hash_get_first ( struct dm_hash_table * t )
{
return _next_slot ( t , 0 ) ;
}
struct dm_hash_node * dm_hash_get_next ( struct dm_hash_table * t , struct dm_hash_node * n )
{
2021-03-07 17:33:04 +03:00
return n - > next ? n - > next : _next_slot ( t , ( n - > hash & t - > mask_slots ) + 1 ) ;
2018-05-14 14:16:43 +03:00
}