/** * Save a hashtable to disk * * @param table Hashtable to save * @param filename Filename to write hashtable into * @param keywrite Pointer to function that writes a single key * @param valuewrite Pointer to function that writes a single value * @return Number of entries written or -1 on error */ int hashtable_save(HASHTABLE *table, char *filename, int (*keywrite)(int, void*), int (*valuewrite)(int, void*)) { int fd, rval = 0; HASHITERATOR *iter; void *key, *value; if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) { return -1; } if (write(fd, "HASHTABLE", 7) != 7) // Magic number { close(fd); return -1; } write(fd, &rval, sizeof(rval)); // Write zero counter, will be overrwriten at end if ((iter = hashtable_iterator(table)) != NULL) { while ((key = hashtable_next(iter)) != NULL) { if (!(*keywrite)(fd, key)) { close(fd); hashtable_iterator_free(iter); return -1; } if ((value = hashtable_fetch(table, key)) == NULL || (*valuewrite)(fd, value) == 0) { close(fd); hashtable_iterator_free(iter); return -1; } rval++; } } /* Now go back and write the count of entries */ if (lseek(fd, 7L, SEEK_SET) != -1) { write(fd, &rval, sizeof(rval)); } close(fd); hashtable_iterator_free(iter); return rval; }
void hashtable_set(hashtable_t* input, const void* key, size_t key_length, void* value) { //fprintf(stderr,"store(%s,%p)\n",key,value); uint32_t hashcode=_hashtable_function(key, key_length); uint32_t bucket=hashcode%input->size; hashelement_t* element=input->buckets[bucket]; #ifdef HASHTABLE_REORDER_ON_ACCESS hashelement_t* previous_element=NULL; #endif #ifdef HASHTABLE_GATHER_STATS unsigned int num_hits=1; #endif while(element!=NULL) { if(element->key_length==key_length && memcmp(element->key,key,key_length)==0) { element->value=value; #ifdef HASHTABLE_GATHER_STATS input->num_hits_per_access+=num_hits; input->num_accesses++; #endif #ifdef HASHTABLE_REORDER_ON_ACCESS if(previous_element!=NULL) { previous_element->next_in_bucket=element->next_in_bucket; element->next_in_bucket=input->buckets[bucket]; input->buckets[bucket]=element; } #endif return; } #ifdef HASHTABLE_GATHER_STATS num_hits++; #endif #ifdef HASHTABLE_REORDER_ON_ACCESS previous_element=element; #endif element=element->next_in_bucket; } #ifdef HASHTABLE_GATHER_STATS input->num_hits_per_access+=num_hits; input->num_accesses++; #endif element = (hashelement_t*)MALLOC(sizeof(hashelement_t)); element->hashcode=hashcode; #ifdef HASHTABLE_CENTRALIZE_KEYS if(input==_hashtable_key_repository) { element->key=key; } else { if(_hashtable_key_repository==NULL) _hashtable_key_repository=hashtable_new(); char* repository_key=hashtable_fetch(_hashtable_key_repository, key, key_length); if(repository_key==NULL) { repository_key=MALLOC(key_length); memcpy(repository_key,key,key_length); hashtable_store(_hashtable_key_repository, repository_key, key_length, repository_key); } element->key=repository_key; } #else element->key=MALLOC(key_length); memcpy(element->key,key,key_length); #endif element->key_length=key_length; element->value=value; element->next_in_bucket=input->buckets[bucket]; input->buckets[bucket]=element; input->length++; #ifdef HASHTABLE_INCREASE_SIZE if(input->length/input->size>input->max_average_collisions)hashtable_resize(input, (size_t)(input->size*(input->resize_factor)+1));//+input->size); #endif }
/** * Fetch the authentication data for a particular user from the users table * * @param users The MySQL users table * @param key The key with user@host * @return The authentication data or NULL on error */ char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key) { if (key == NULL) return NULL; atomic_add(&users->stats.n_fetches, 1); return hashtable_fetch(users->data, key); }
/* main */ int main(int argc, char *argv[]) { struct hashtable * hashtable; struct hashtable_iterator iter; struct hashtable_entry entry; void * valuepointer; int valueint, seed = 0, stringcount = 0, stringlength, key_bytes = 0, value_bytes = 0, entrynumber, collisions = 0, delete_count; char * source, * destination, * value; diag("02b-hashtable"); plan(4); hashtable = hashtable_new(); srand(seed); /* TODO: get a portable seed from for example current time */ while (stringcount<STRINGCOUNT) { /* nondeterministic because of collisions */ char * key = random_string(MAXKEYLENGTH); /* create a value consisting of the key reversed followed by */ /* the original key, for example 'abc' -> 'cbaabc' */ stringlength = strlen(key); value = (char *) malloc(2 * stringlength + 1); destination=value+stringlength; * destination -- = '\0'; for (source=key; stringlength-->0; ) { * destination -- = * source ++; } strcat( value, key ); /* test whether the key is already in the hashtable */ if ( hashtable_fetch(hashtable, key, strlen(key), & valuepointer, & valueint) ) { /* it is already in the hash table, free these values */ free(key); free(value); ++ collisions; } else { /* it is not already in the hash table, add it */ hashtable_store(hashtable, key, strlen(key), value, strlen(value)); key_bytes += strlen(key); value_bytes += strlen(value); ++ stringcount; } } is_ii( stringcount, STRINGCOUNT, "created a hash with 5000 entries"); srand(seed); /* Test 2 - iterate the entries and delete them */ hashtable_iterator_init(hashtable, & iter); delete_count = 0; while (hashtable_iterator_next(& iter, & entry)) { key_bytes -= strlen(entry.keypointer); value_bytes -= strlen(entry.valuepointer); /* fprintf(stderr,"iter A '%s' => '%s'\n", (char *) entry.keypointer, (char *) entry.valuepointer); */ free(entry.keypointer); free(entry.valuepointer); ++delete_count; } is_ii(delete_count, stringcount, "iterate 5000 entries and delete"); /* Test 3 - verify total number of bytes in keys */ is_ii(key_bytes, 0, "all bytes in keys reclaimed"); /* Test 4 - verify total number of bytes in keys */ is_ii(value_bytes, 0, "all bytes in values reclaimed"); /* Cannot test this internally, but Valgrind should show that no */ /* bytes remain allocated on the heap. */ hashtable_free(hashtable); return 0; }