strongswan/src/libstrongswan/collections/hashtable.c

477 lines
8.3 KiB
C
Raw Normal View History

2008-12-03 09:32:16 +00:00
/*
* Copyright (C) 2008-2012 Tobias Brunner
2008-12-03 09:32:16 +00:00
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*/
#include "hashtable.h"
#include <utils/chunk.h>
2008-12-03 09:32:16 +00:00
/** The maximum capacity of the hash table (MUST be a power of 2) */
#define MAX_CAPACITY (1 << 30)
typedef struct pair_t pair_t;
/**
* This pair holds a pointer to the key and value it represents.
*/
struct pair_t {
/**
* Key of a hash table item.
*/
const void *key;
2008-12-03 09:32:16 +00:00
/**
* Value of a hash table item.
*/
void *value;
2008-12-03 09:32:16 +00:00
/**
* Cached hash (used in case of a resize).
*/
u_int hash;
/**
* Next pair in an overflow list.
*/
pair_t *next;
2008-12-03 09:32:16 +00:00
};
/**
* Creates an empty pair object.
*/
static inline pair_t *pair_create(const void *key, void *value, u_int hash)
2008-12-03 09:32:16 +00:00
{
pair_t *this;
INIT(this,
.key = key,
.value = value,
.hash = hash,
);
2008-12-03 09:32:16 +00:00
return this;
}
typedef struct private_hashtable_t private_hashtable_t;
/**
* Private data of a hashtable_t object.
*
*/
struct private_hashtable_t {
/**
* Public part of hash table.
*/
hashtable_t public;
2008-12-03 09:32:16 +00:00
/**
* The number of items in the hash table.
2008-12-03 09:32:16 +00:00
*/
u_int count;
2008-12-03 09:32:16 +00:00
/**
* The current capacity of the hash table (always a power of 2).
*/
u_int capacity;
2008-12-03 09:32:16 +00:00
/**
* The current mask to calculate the row index (capacity - 1).
2008-12-03 09:32:16 +00:00
*/
u_int mask;
2008-12-03 09:32:16 +00:00
/**
* The load factor.
*/
float load_factor;
2008-12-03 09:32:16 +00:00
/**
* The actual table.
*/
pair_t **table;
2008-12-03 09:32:16 +00:00
/**
* The hashing function.
*/
hashtable_hash_t hash;
2008-12-03 09:32:16 +00:00
/**
* The equality function.
*/
hashtable_equals_t equals;
};
typedef struct private_enumerator_t private_enumerator_t;
/**
* hash table enumerator implementation
*/
struct private_enumerator_t {
/**
* implements enumerator interface
*/
enumerator_t enumerator;
2008-12-03 09:32:16 +00:00
/**
* associated hash table
*/
private_hashtable_t *table;
2008-12-03 09:32:16 +00:00
/**
* current row index
*/
u_int row;
/**
* number of remaining items in hashtable
*/
u_int count;
/**
* current pair
*/
pair_t *current;
2008-12-03 09:32:16 +00:00
/**
* previous pair (used by remove_at)
2008-12-03 09:32:16 +00:00
*/
pair_t *prev;
};
2008-12-03 09:32:16 +00:00
/*
* See header.
*/
u_int hashtable_hash_ptr(const void *key)
{
return chunk_hash(chunk_from_thing(key));
}
/*
* See header.
*/
u_int hashtable_hash_str(const void *key)
{
return chunk_hash(chunk_from_str((char*)key));
}
/*
* See header.
*/
bool hashtable_equals_ptr(const void *key, const void *other_key)
{
return key == other_key;
}
/*
* See header.
*/
bool hashtable_equals_str(const void *key, const void *other_key)
{
return streq(key, other_key);
}
2008-12-03 09:32:16 +00:00
/**
* This function returns the next-highest power of two for the given number.
* The algorithm works by setting all bits on the right-hand side of the most
* significant 1 to 1 and then increments the whole number so it rolls over
* to the nearest power of two. Note: returns 0 for n == 0
*/
static u_int get_nearest_powerof2(u_int n)
{
u_int i;
2009-10-26 15:08:14 +00:00
2008-12-03 09:32:16 +00:00
--n;
2008-12-04 16:33:39 +00:00
for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
2008-12-03 09:32:16 +00:00
{
n |= n >> i;
}
return ++n;
}
/**
* Init hash table parameters
*/
static void init_hashtable(private_hashtable_t *this, u_int capacity)
{
capacity = max(1, min(capacity, MAX_CAPACITY));
this->capacity = get_nearest_powerof2(capacity);
this->mask = this->capacity - 1;
this->load_factor = 0.75;
this->table = calloc(this->capacity, sizeof(pair_t*));
2008-12-03 09:32:16 +00:00
}
/**
* Double the size of the hash table and rehash all the elements.
*/
static void rehash(private_hashtable_t *this)
{
pair_t **old_table;
2009-10-26 15:08:14 +00:00
u_int row, old_capacity;
if (this->capacity >= MAX_CAPACITY)
2008-12-03 09:32:16 +00:00
{
return;
}
2009-10-26 15:08:14 +00:00
old_capacity = this->capacity;
old_table = this->table;
2008-12-03 09:32:16 +00:00
init_hashtable(this, old_capacity << 1);
2009-10-26 15:08:14 +00:00
for (row = 0; row < old_capacity; row++)
2008-12-03 09:32:16 +00:00
{
pair_t *pair, *next;
2009-10-26 15:08:14 +00:00
u_int new_row;
pair = old_table[row];
while (pair)
{ /* insert pair at the front of new bucket*/
next = pair->next;
new_row = pair->hash & this->mask;
pair->next = this->table[new_row];
this->table[new_row] = pair;
pair = next;
2008-12-03 09:32:16 +00:00
}
}
free(old_table);
}
METHOD(hashtable_t, put, void*,
private_hashtable_t *this, const void *key, void *value)
2008-12-03 09:32:16 +00:00
{
void *old_value = NULL;
pair_t *pair;
u_int hash, row;
2009-10-26 15:08:14 +00:00
hash = this->hash(key);
row = hash & this->mask;
pair = this->table[row];
while (pair)
{ /* search existing bucket for key */
if (this->equals(key, pair->key))
2008-12-03 09:32:16 +00:00
{
old_value = pair->value;
pair->value = value;
pair->key = key;
break;
2008-12-03 09:32:16 +00:00
}
pair = pair->next;
2008-12-03 09:32:16 +00:00
}
if (!pair)
{ /* insert at the front of bucket */
pair = pair_create(key, value, hash);
pair->next = this->table[row];
this->table[row] = pair;
2008-12-03 09:32:16 +00:00
this->count++;
}
if (this->count >= this->capacity * this->load_factor)
{
rehash(this);
}
return old_value;
}
static void *get_internal(private_hashtable_t *this, const void *key,
hashtable_equals_t equals)
2008-12-03 09:32:16 +00:00
{
void *value = NULL;
2009-10-26 15:08:14 +00:00
pair_t *pair;
if (!this->count)
{ /* no need to calculate the hash */
return NULL;
}
pair = this->table[this->hash(key) & this->mask];
while (pair)
2008-12-03 09:32:16 +00:00
{
if (equals(key, pair->key))
2008-12-03 09:32:16 +00:00
{
value = pair->value;
break;
2008-12-03 09:32:16 +00:00
}
pair = pair->next;
2008-12-03 09:32:16 +00:00
}
return value;
}
METHOD(hashtable_t, get, void*,
private_hashtable_t *this, const void *key)
{
return get_internal(this, key, this->equals);
}
METHOD(hashtable_t, get_match, void*,
private_hashtable_t *this, const void *key, hashtable_equals_t match)
{
return get_internal(this, key, match);
}
METHOD(hashtable_t, remove_, void*,
private_hashtable_t *this, const void *key)
2008-12-03 09:32:16 +00:00
{
void *value = NULL;
pair_t *pair, *prev = NULL;
u_int row;
row = this->hash(key) & this->mask;
pair = this->table[row];
while (pair)
2008-12-03 09:32:16 +00:00
{
if (this->equals(key, pair->key))
2008-12-03 09:32:16 +00:00
{
if (prev)
{
prev->next = pair->next;
}
else
2008-12-03 09:32:16 +00:00
{
this->table[row] = pair->next;
2008-12-03 09:32:16 +00:00
}
value = pair->value;
this->count--;
free(pair);
break;
2008-12-03 09:32:16 +00:00
}
prev = pair;
pair = pair->next;
2008-12-03 09:32:16 +00:00
}
return value;
}
METHOD(hashtable_t, remove_at, void,
private_hashtable_t *this, private_enumerator_t *enumerator)
{
if (enumerator->table == this && enumerator->current)
{
pair_t *current = enumerator->current;
if (enumerator->prev)
{
enumerator->prev->next = current->next;
}
else
{
this->table[enumerator->row] = current->next;
}
enumerator->current = enumerator->prev;
free(current);
this->count--;
}
}
METHOD(hashtable_t, get_count, u_int,
private_hashtable_t *this)
2008-12-03 09:32:16 +00:00
{
return this->count;
}
METHOD(enumerator_t, enumerate, bool,
private_enumerator_t *this, const void **key, void **value)
2008-12-03 09:32:16 +00:00
{
while (this->count && this->row < this->table->capacity)
2008-12-03 09:32:16 +00:00
{
this->prev = this->current;
2008-12-03 09:32:16 +00:00
if (this->current)
{
this->current = this->current->next;
2008-12-03 09:32:16 +00:00
}
else
{
this->current = this->table->table[this->row];
}
if (this->current)
{
if (key)
2008-12-03 09:32:16 +00:00
{
*key = this->current->key;
2008-12-03 09:32:16 +00:00
}
if (value)
{
*value = this->current->value;
}
this->count--;
return TRUE;
2008-12-03 09:32:16 +00:00
}
this->row++;
}
return FALSE;
}
METHOD(hashtable_t, create_enumerator, enumerator_t*,
private_hashtable_t *this)
2008-12-03 09:32:16 +00:00
{
private_enumerator_t *enumerator;
INIT(enumerator,
.enumerator = {
.enumerate = (void*)_enumerate,
.destroy = (void*)free,
},
.table = this,
.count = this->count,
);
2008-12-03 09:32:16 +00:00
return &enumerator->enumerator;
}
METHOD(hashtable_t, destroy, void,
private_hashtable_t *this)
2008-12-03 09:32:16 +00:00
{
pair_t *pair, *next;
2008-12-03 09:32:16 +00:00
u_int row;
2009-10-26 15:08:14 +00:00
for (row = 0; row < this->capacity; row++)
2008-12-03 09:32:16 +00:00
{
pair = this->table[row];
while (pair)
2008-12-03 09:32:16 +00:00
{
next = pair->next;
free(pair);
pair = next;
2008-12-03 09:32:16 +00:00
}
}
free(this->table);
free(this);
}
/*
* Described in header.
*/
hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
u_int capacity)
{
private_hashtable_t *this;
INIT(this,
.public = {
.put = _put,
.get = _get,
.get_match = _get_match,
.remove = _remove_,
.remove_at = (void*)_remove_at,
.get_count = _get_count,
.create_enumerator = _create_enumerator,
.destroy = _destroy,
},
.hash = hash,
.equals = equals,
);
2008-12-03 09:32:16 +00:00
init_hashtable(this, capacity);
2008-12-03 09:32:16 +00:00
return &this->public;
}