2007-07-13 19:37:47 +00:00
/* Hash table with chaining. */
# include "hashtable.h"
# include "memory.h"
# include "../include/ydb-constants.h"
# include <assert.h>
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include "key.h"
# include "yerror.h"
2007-08-13 18:59:09 +00:00
# include "hashfun.h"
2007-07-13 19:37:47 +00:00
2007-07-24 15:08:05 +00:00
int toku_hashtable_create ( HASHTABLE * h ) {
2007-07-13 19:37:47 +00:00
HASHTABLE MALLOC ( tab ) ;
int i ;
if ( tab = = 0 ) return - 1 ;
tab - > n_keys = 0 ;
2007-07-30 21:44:27 +00:00
tab - > arraysize = 8 ;
2007-07-13 19:37:47 +00:00
assert ( sizeof ( * tab - > array ) = = sizeof ( void * ) ) ;
2007-07-20 18:00:14 +00:00
tab - > array = toku_calloc ( tab - > arraysize , sizeof ( * tab - > array ) ) ;
2007-07-13 19:37:47 +00:00
for ( i = 0 ; i < tab - > arraysize ; i + + ) tab - > array [ i ] = 0 ;
* h = tab ;
return 0 ;
}
2007-08-13 19:49:06 +00:00
static void hash_find_internal ( HASHTABLE tab , unsigned int hash , const unsigned char * key , ITEMLEN keylen , HASHELT * hashelt , HASHELT * * prev_ptr ) {
unsigned int h = hash % tab - > arraysize ;
2007-07-13 19:37:47 +00:00
HASHELT he ;
HASHELT * prev = & tab - > array [ h ] ;
for ( he = * prev ; he ; prev = & he - > next , he = * prev ) {
if ( keylen = = he - > keylen & & memcmp ( key , he - > key , keylen ) = = 0 ) {
* prev_ptr = prev ;
* hashelt = he ;
return ;
}
}
* prev_ptr = prev ;
* hashelt = 0 ;
}
2007-07-24 15:08:05 +00:00
int toku_hash_find ( HASHTABLE tab , bytevec key , ITEMLEN keylen , bytevec * data , ITEMLEN * datalen ) {
2007-07-13 19:37:47 +00:00
HASHELT he , * prev_ptr ;
2007-08-13 19:49:06 +00:00
hash_find_internal ( tab , hash_key ( key , keylen ) , key , keylen , & he , & prev_ptr ) ;
2007-07-13 19:37:47 +00:00
if ( he = = 0 ) {
return - 1 ;
} else {
* data = he - > val ;
* datalen = he - > vallen ;
return 0 ;
}
}
2007-07-24 20:23:09 +00:00
int toku_hash_rehash_everything ( HASHTABLE tab , int newarraysize ) {
HASHELT * newarray = toku_calloc ( newarraysize , sizeof ( * tab - > array ) ) ;
int i ;
assert ( newarray ! = 0 ) ;
for ( i = 0 ; i < newarraysize ; i + + ) newarray [ i ] = 0 ;
for ( i = 0 ; i < tab - > arraysize ; i + + ) {
HASHELT he ;
while ( ( he = tab - > array [ i ] ) ! = 0 ) {
2007-08-13 19:49:06 +00:00
//unsigned int hk = hash_key((unsigned char *)he->key, he->keylen);
unsigned int h = he - > hash % newarraysize ;
//assert(he->hash==hk);
2007-07-24 20:23:09 +00:00
tab - > array [ i ] = he - > next ;
he - > next = newarray [ h ] ;
newarray [ h ] = he ;
}
}
toku_free ( tab - > array ) ;
// printf("Freed\n");
tab - > array = newarray ;
tab - > arraysize = newarraysize ;
//printf("Done growing or shrinking\n");
return 0 ;
}
2007-07-13 19:37:47 +00:00
2007-08-13 19:04:38 +00:00
int toku_hash_insert ( HASHTABLE tab , const void * key , ITEMLEN keylen , const void * val , ITEMLEN vallen )
2007-07-13 19:37:47 +00:00
{
2007-08-13 19:49:06 +00:00
unsigned int hk = hash_key ( key , keylen ) ;
unsigned int h = hk % tab - > arraysize ;
2007-07-13 19:37:47 +00:00
{
HASHELT he , * prev_ptr ;
2007-08-13 19:49:06 +00:00
hash_find_internal ( tab , hk , key , keylen , & he , & prev_ptr ) ;
2007-07-13 19:37:47 +00:00
if ( he ! = 0 ) {
return BRT_ALREADY_THERE ;
}
}
{
/* Otherwise the key is not already present, so we need to add it. */
HASHELT MALLOC ( he ) ;
he - > key = memdup ( key , keylen ) ;
he - > keylen = keylen ;
he - > val = memdup ( val , vallen ) ;
he - > vallen = vallen ;
2007-08-13 19:49:06 +00:00
he - > hash = hk ;
2007-07-13 19:37:47 +00:00
he - > next = tab - > array [ h ] ;
tab - > array [ h ] = he ;
tab - > n_keys + + ;
if ( tab - > n_keys > tab - > arraysize ) {
2007-07-24 20:23:09 +00:00
return toku_hash_rehash_everything ( tab , tab - > arraysize * 2 ) ;
2007-07-13 19:37:47 +00:00
}
return BRT_OK ;
}
}
2007-08-13 19:04:38 +00:00
int toku_hash_delete ( HASHTABLE tab , const void * key , ITEMLEN keylen ) {
2007-07-13 19:37:47 +00:00
HASHELT he , * prev_ptr ;
//printf("%s:%d deleting %s (bucket %d)\n", __FILE__, __LINE__, key, hash_key(key,keylen)%tab->arraysize);
2007-08-13 19:49:06 +00:00
hash_find_internal ( tab , hash_key ( key , keylen ) , key , keylen , & he , & prev_ptr ) ;
2007-07-13 19:37:47 +00:00
if ( he = = 0 ) return DB_NOTFOUND ;
else {
//printf("%s:%d deleting %s %s\n", __FILE__, __LINE__, he->key, he->val);
assert ( * prev_ptr = = he ) ;
* prev_ptr = he - > next ;
//printf("Freeing %s %s\n", he->key, he->val);
2007-08-08 14:59:09 +00:00
toku_free_n ( he - > key , he - > keylen ) ;
toku_free_n ( he - > val , he - > vallen ) ;
toku_free_n ( he , sizeof ( * he ) ) ;
2007-07-13 19:37:47 +00:00
tab - > n_keys - - ;
2007-07-24 20:23:09 +00:00
if ( ( tab - > n_keys * 4 < tab - > arraysize ) & & tab - > arraysize > 4 ) {
return toku_hash_rehash_everything ( tab , tab - > arraysize / 2 ) ;
}
2007-07-13 19:37:47 +00:00
return BRT_OK ;
}
}
2007-07-24 15:08:05 +00:00
int toku_hashtable_random_pick ( HASHTABLE h , bytevec * key , ITEMLEN * keylen , bytevec * data , ITEMLEN * datalen ) {
2007-07-13 19:37:47 +00:00
int i ;
2007-07-24 20:23:09 +00:00
int usei = random ( ) % h - > arraysize ;
for ( i = 0 ; i < h - > arraysize ; i + + , usei + + ) {
2007-07-30 21:44:27 +00:00
if ( usei > = h - > arraysize ) usei = 0 ;
2007-07-24 20:23:09 +00:00
HASHELT he = h - > array [ usei ] ;
2007-07-13 19:37:47 +00:00
if ( he ) {
* key = he - > key ;
* keylen = he - > keylen ;
* data = he - > val ;
* datalen = he - > vallen ;
return 0 ;
}
}
return - 1 ;
}
#if 0
int hashtable_find_last ( HASHTABLE h , bytevec * key , ITEMLEN * keylen , bytevec * data , ITEMLEN * datalen ) {
bytevec best_k = 0 , best_d ;
ITEMLEN best_kl , best_dl ;
HASHTABLE_ITERATE ( h , this_k , this_kl , this_d , this_dl ,
( {
if ( best_k = = 0 | | keycompare ( best_k , best_kl , this_k , this_kl ) < 0 ) {
best_k = this_k ;
best_kl = this_kl ;
best_d = this_d ;
best_dl = this_dl ;
}
} ) ) ;
if ( best_k ) {
* key = best_k ;
* keylen = best_kl ;
* data = best_d ;
* datalen = best_dl ;
return 0 ;
} else {
return - 1 ;
}
}
# endif
2007-07-24 15:08:05 +00:00
void toku_hashtable_iterate ( HASHTABLE tab , void ( * f ) ( bytevec key , ITEMLEN keylen , bytevec data , ITEMLEN datalen , void * args ) , void * args ) {
2007-07-13 19:37:47 +00:00
/*
int i ;
for ( i = 0 ; i < tab - > arraysize ; i + + ) {
HASHELT he ;
for ( he = tab - > array [ i ] ; he ; he = he - > next ) {
f ( he - > key , he - > keylen , he - > val , he - > vallen , args ) ;
}
}
*/
HASHTABLE_ITERATE ( tab , key , keylen , val , vallen , f ( key , keylen , val , vallen , args ) ) ;
}
2007-07-24 15:08:05 +00:00
int toku_hashtable_n_entries ( HASHTABLE tab ) {
2007-07-13 19:37:47 +00:00
return tab - > n_keys ;
}
/* Frees the list, but doesn't free the keys. */
static void hasheltlist_free ( HASHELT elt ) {
if ( elt = = 0 ) return ;
else {
hasheltlist_free ( elt - > next ) ;
2007-08-08 14:59:09 +00:00
toku_free_n ( elt - > key , elt - > keylen ) ;
toku_free_n ( elt - > val , elt - > vallen ) ;
toku_free_n ( elt , sizeof ( * elt ) ) ;
2007-07-13 19:37:47 +00:00
}
}
/* Frees the table, but doesn't do anything to the contents of the table. The keys are still alloc'd. The internal storage of the hashtable is freed. */
2007-07-24 15:08:05 +00:00
void toku_hashtable_free ( HASHTABLE * tab ) {
2007-07-13 19:37:47 +00:00
//printf("%s:%d free hashtable %p\n", __FILE__, __LINE__, tab);
2007-07-24 15:08:05 +00:00
toku_hashtable_clear ( * tab ) ;
2007-07-13 19:37:47 +00:00
//printf("%s:%d free %p\n", __FILE__, __LINE__, tab);n
2007-07-20 18:00:14 +00:00
toku_free ( ( * tab ) - > array ) ;
2007-08-08 14:59:09 +00:00
toku_free_n ( * tab , sizeof ( * * tab ) ) ;
2007-07-13 19:37:47 +00:00
* tab = 0 ;
}
2007-07-24 15:08:05 +00:00
void toku_hashtable_clear ( HASHTABLE tab ) {
2007-07-13 19:37:47 +00:00
int i ;
for ( i = 0 ; i < tab - > arraysize ; i + + ) {
hasheltlist_free ( tab - > array [ i ] ) ;
tab - > array [ i ] = 0 ;
}
tab - > n_keys = 0 ;
}