/* $Id: hash.c 386 2004-11-18 01:49:29Z massie $ */ #include #include #include #include "hash.h" #include "debug_msg.h" datum_t * datum_new ( void *data, size_t size ) { datum_t *datum; datum = malloc( sizeof(datum_t)); if ( datum == NULL ) return NULL; datum->data = malloc( size ); if ( datum->data == NULL ) { free(datum); return NULL; } datum->size = size; memcpy(datum->data, data, datum->size); return datum; } static datum_t * datum_dup (datum_t *src) { datum_t *dest; dest = malloc(sizeof(datum_t)); if( dest == NULL ) return NULL; dest->data = malloc(src->size); if ( dest->data == NULL ) { free(dest); return NULL; } dest->size = src->size; memcpy(dest->data, src->data, src->size); return dest; } void datum_free (datum_t *datum) { free(datum->data); free(datum); } static size_t hash_prime (size_t size) { size_t i, num_primes; /* http://www.mathematical.com/primes1to100k.html */ int primes[]={ 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97, 101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199, 211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293, 307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397, 401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499, 503,509,521,523,541,547,557,563,569,571,577,587,593,599, 601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691, 701,709,719,727,733,739,743,751,757,761,769,773,787,797, 809,811,821,823,827,829,839,853,857,859,863,877,881,883,887, 907,911,919,929,937,941,947,953,967,971,977,983,991,997, 1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097, 1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193, 1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297, 1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399, 1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499, 1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597, 1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699, 1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789, 1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889, 1901,1907,1193,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999, 2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099, 2111,2113,2129,2131,2137,2141,2143,2153,2161,2179, 2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297, 2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399 }; num_primes = sizeof(primes)/sizeof(int); for(i=0; i < num_primes ; i++) { if( primes[i] > size ) return primes[i]; } return primes[num_primes-1]; } hash_t * hash_create (size_t size) { size_t i; hash_t *hash; debug_msg("hash_create size = %d", size); hash = (hash_t *) malloc ( sizeof(hash_t) ); if( hash == NULL ) { debug_msg("hash malloc error in hash_create()"); return NULL; } hash->size = hash_prime(size); debug_msg("hash->size is %d", hash->size); hash->node = (node_t * *) malloc (sizeof (node_t *) * hash->size); if (hash->node == NULL) { debug_msg("hash->node malloc error. freeing hash."); free(hash); return NULL; } for (i = 0; i < hash->size; i++) { hash->node[i] = malloc( sizeof(node_t) ); if ( hash->node[i] == NULL ) break; /* Initialize */ hash->node[i]->bucket = NULL; pthread_rdwr_init_np( &(hash->node[i]->rwlock) ); } /* Was there an error initializing the hash nodes? */ if ( i != hash->size ) { debug_msg("hash->node[i] malloc error"); /* Rewind */ for (hash->size = i; hash->size >= 0; hash->size--) { free(hash->node[hash->size]); } free(hash); return NULL; } return hash; } void hash_destroy (hash_t * hash) { size_t i; bucket_t *bucket, *next; datum_t *val; for( i=0; i< hash->size; i++) { for(bucket = hash->node[i]->bucket; bucket!= NULL; bucket = next) { next = bucket->next; val = hash_delete( bucket->key, hash); datum_free(val); } free(hash->node[i]); } free( hash->node ); free( hash ); } size_t hashval ( datum_t *key, hash_t *hash ) { int i; int hash_val; /* We should handle these errors better later */ if ( hash == NULL || key == NULL || key->data == NULL || key->size <= 0 ) return 0; hash_val = ((unsigned char *)key->data)[0]; for ( i = 0; i < key->size ; i++ ) hash_val = ( hash_val * 32 + ((unsigned char *)key->data)[i]) % hash->size; return hash_val; } datum_t * hash_insert (datum_t *key, datum_t *val, hash_t *hash) { size_t i; bucket_t *bucket; i = hashval(key, hash); WRITE_LOCK(hash, i); bucket = hash->node[i]->bucket; if ( bucket == NULL ) { /* This bucket hasn't been used yet */ bucket = (bucket_t *)malloc( sizeof(bucket_t) ); if ( bucket == NULL ) { WRITE_UNLOCK(hash, i); return NULL; } bucket->next = NULL; bucket->key = datum_dup(key); if ( bucket->key == NULL ) { free(bucket); bucket = NULL; WRITE_UNLOCK(hash, i); return NULL; } bucket->val = datum_dup(val); if ( bucket->val == NULL ) { free(bucket); bucket = NULL; WRITE_UNLOCK(hash, i); return NULL; } hash->node[i]->bucket = bucket; WRITE_UNLOCK(hash, i); return bucket->val; } /* This node in the hash is already in use. Collision or new data for existing key. */ for (bucket = hash->node[i]->bucket; bucket != NULL; bucket = bucket->next) { if( bucket->key->size != key->size ) continue; if(! strncmp(bucket->key->data, key->data, key->size) ) { /* New data for an existing key */ /* Make sure we have enough space */ if ( bucket->val->size != val->size ) { /* Make sure we have enough room */ if(! (bucket->val->data = realloc(bucket->val->data, val->size)) ) { WRITE_UNLOCK(hash, i); return NULL; } bucket->val->size = val->size; } memcpy( bucket->val->data, val->data, val->size ); WRITE_UNLOCK(hash, i); return bucket->val; } } /* It's a Hash collision... link it in the collided bucket */ bucket = (bucket_t *) malloc (sizeof (bucket_t)); if (bucket == NULL) { WRITE_UNLOCK(hash, i); return NULL; } bucket->key = datum_dup (key); if ( bucket->key == NULL ) { free(bucket); WRITE_UNLOCK(hash, i); return NULL; } bucket->val = datum_dup (val); if ( bucket->val == NULL ) { datum_free(bucket->key); free(bucket); WRITE_UNLOCK(hash, i); return NULL; } bucket->next = hash->node[i]->bucket; hash->node[i]->bucket = bucket; WRITE_UNLOCK(hash, i); return bucket->val; } datum_t * hash_lookup (datum_t *key, hash_t * hash) { size_t i; datum_t *val; bucket_t *bucket; i = hashval(key, hash); READ_LOCK(hash, i); bucket = hash->node[i]->bucket; if ( bucket == NULL ) { READ_UNLOCK(hash, i); return NULL; } for (; bucket != NULL; bucket = bucket->next) { if ( key->size != bucket->key->size ) continue; if (! memcmp( key->data, bucket->key->data, key->size )) { val = datum_dup( bucket->val ); READ_UNLOCK(hash, i); return val; } } READ_UNLOCK(hash, i); return NULL; } datum_t * hash_delete (datum_t *key, hash_t * hash) { size_t i; datum_t *val; bucket_t *bucket, *last = NULL; i = hashval(key,hash); WRITE_LOCK(hash,i); if ( hash->node[i]->bucket == NULL ) { WRITE_UNLOCK(hash,i); return NULL; } for (last = NULL, bucket = hash->node[i]->bucket; bucket != NULL; last = bucket, bucket = bucket->next) { if (bucket->key->size == key->size && !strncmp (key->data, bucket->key->data, key->size)) { if (last != NULL) { val = bucket->val; last->next = bucket->next; datum_free(bucket->key); free (bucket); WRITE_UNLOCK(hash,i); return val; } else { val = bucket->val; hash->node[i]->bucket = bucket->next; datum_free (bucket->key); free (bucket); WRITE_UNLOCK(hash,i); return val; } } } WRITE_UNLOCK(hash,i); return NULL; } /* Walk the hash table from hash index "from" until the end, or * until stopped by walk function. Similar to hash_foreach, used by cleanup. * Use of hint makes O(n^2) cleanup into O(n). */ int hash_walkfrom (hash_t * hash, size_t from, int (*func)(datum_t *, datum_t *, void *), void *arg) { int stop=0; size_t i; bucket_t *bucket; for (i = from; i < hash->size && !stop; i++) { READ_LOCK(hash, i); for (bucket = hash->node[i]->bucket; bucket != NULL; bucket = bucket->next) { stop = func(bucket->key, bucket->val, arg); if (stop) break; } READ_UNLOCK(hash, i); } return stop; } int hash_foreach (hash_t * hash, int (*func)(datum_t *, datum_t *, void *), void *arg) { int stop=0; size_t i; bucket_t *bucket; for (i = 0; i < hash->size && !stop; i++) { READ_LOCK(hash, i); for (bucket = hash->node[i]->bucket; bucket != NULL; bucket = bucket->next) { stop = func(bucket->key, bucket->val, arg); if (stop) break; } READ_UNLOCK(hash, i); } return stop; }