hashlist: Move get_match() and sorting into a separate class
The main intention here is that we can change the hashtable_t implementation without being impeded by the special requirements imposed by get_match() and sorting the keys/items in buckets.
This commit is contained in:
parent
4334f61284
commit
d9944102f5
|
@ -330,12 +330,12 @@ struct private_kernel_netlink_net_t {
|
|||
/**
|
||||
* Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
|
||||
*/
|
||||
hashtable_t *addrs;
|
||||
hashlist_t *addrs;
|
||||
|
||||
/**
|
||||
* Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t)
|
||||
*/
|
||||
hashtable_t *vips;
|
||||
hashlist_t *vips;
|
||||
|
||||
/**
|
||||
* netlink rt socket (routing)
|
||||
|
@ -375,7 +375,7 @@ struct private_kernel_netlink_net_t {
|
|||
/**
|
||||
* installed routes
|
||||
*/
|
||||
hashtable_t *routes;
|
||||
hashlist_t *routes;
|
||||
|
||||
/**
|
||||
* mutex for routes
|
||||
|
@ -499,7 +499,7 @@ static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this)
|
|||
this->net_changes_lock->lock(this->net_changes_lock);
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
|
||||
enumerator = this->routes->create_enumerator(this->routes);
|
||||
enumerator = this->routes->ht.create_enumerator(&this->routes->ht);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&route))
|
||||
{
|
||||
net_change_t *change, lookup = {
|
||||
|
@ -617,7 +617,7 @@ static bool is_known_vip(private_kernel_netlink_net_t *this, host_t *ip)
|
|||
/**
|
||||
* Add an address map entry
|
||||
*/
|
||||
static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr,
|
||||
static void addr_map_entry_add(hashlist_t *map, addr_entry_t *addr,
|
||||
iface_entry_t *iface)
|
||||
{
|
||||
addr_map_entry_t *entry;
|
||||
|
@ -627,14 +627,14 @@ static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr,
|
|||
.addr = addr,
|
||||
.iface = iface,
|
||||
);
|
||||
entry = map->put(map, entry, entry);
|
||||
entry = map->ht.put(&map->ht, entry, entry);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an address map entry
|
||||
*/
|
||||
static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr,
|
||||
static void addr_map_entry_remove(hashlist_t *map, addr_entry_t *addr,
|
||||
iface_entry_t *iface)
|
||||
{
|
||||
addr_map_entry_t *entry, lookup = {
|
||||
|
@ -643,7 +643,7 @@ static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr,
|
|||
.iface = iface,
|
||||
};
|
||||
|
||||
entry = map->remove(map, &lookup);
|
||||
entry = map->ht.remove(&map->ht, &lookup);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
|
@ -1241,7 +1241,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||
};
|
||||
addr_entry_t *addr;
|
||||
|
||||
entry = this->vips->get(this->vips, &lookup);
|
||||
entry = this->vips->ht.get(&this->vips->ht, &lookup);
|
||||
if (entry)
|
||||
{
|
||||
if (hdr->nlmsg_type == RTM_NEWADDR)
|
||||
|
@ -1261,7 +1261,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||
host->destroy(host);
|
||||
return;
|
||||
}
|
||||
entry = this->addrs->get(this->addrs, &lookup);
|
||||
entry = this->addrs->ht.get(&this->addrs->ht, &lookup);
|
||||
if (entry)
|
||||
{
|
||||
if (hdr->nlmsg_type == RTM_DELADDR)
|
||||
|
@ -2726,7 +2726,7 @@ METHOD(kernel_net_t, add_route, status_t,
|
|||
}
|
||||
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
found = this->routes->get(this->routes, &lookup.route);
|
||||
found = this->routes->ht.get(&this->routes->ht, &lookup.route);
|
||||
if (found)
|
||||
{
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
|
@ -2755,7 +2755,7 @@ METHOD(kernel_net_t, add_route, status_t,
|
|||
if (status == SUCCESS)
|
||||
{
|
||||
found = route_entry_clone(&lookup.route);
|
||||
this->routes->put(this->routes, found, found);
|
||||
this->routes->ht.put(&this->routes->ht, found, found);
|
||||
}
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
return status;
|
||||
|
@ -2785,7 +2785,7 @@ METHOD(kernel_net_t, del_route, status_t,
|
|||
}
|
||||
|
||||
this->routes_lock->lock(this->routes_lock);
|
||||
found = this->routes->remove(this->routes, &lookup.route);
|
||||
found = this->routes->ht.remove(&this->routes->ht, &lookup.route);
|
||||
if (!found)
|
||||
{
|
||||
this->routes_lock->unlock(this->routes_lock);
|
||||
|
@ -3023,18 +3023,9 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
|
|||
/**
|
||||
* Destroy an address to iface map
|
||||
*/
|
||||
static void addr_map_destroy(hashtable_t *map)
|
||||
static void addr_map_destroy(hashlist_t *map)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
addr_map_entry_t *addr;
|
||||
|
||||
enumerator = map->create_enumerator(map);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
|
||||
{
|
||||
free(addr);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
map->destroy(map);
|
||||
map->ht.destroy_function(&map->ht, (void*)free);
|
||||
}
|
||||
|
||||
METHOD(kernel_net_t, destroy, void,
|
||||
|
@ -3055,7 +3046,7 @@ METHOD(kernel_net_t, destroy, void,
|
|||
lib->watcher->remove(lib->watcher, this->socket_events);
|
||||
close(this->socket_events);
|
||||
}
|
||||
enumerator = this->routes->create_enumerator(this->routes);
|
||||
enumerator = this->routes->ht.create_enumerator(&this->routes->ht);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&route))
|
||||
{
|
||||
manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen,
|
||||
|
@ -3112,15 +3103,15 @@ kernel_netlink_net_t *kernel_netlink_net_create()
|
|||
lib->settings->get_bool(lib->settings,
|
||||
"%s.plugins.kernel-netlink.parallel_route", FALSE, lib->ns)),
|
||||
.rt_exclude = linked_list_create(),
|
||||
.routes = hashtable_create((hashtable_hash_t)route_entry_hash,
|
||||
(hashtable_equals_t)route_entry_equals, 16),
|
||||
.routes = hashlist_create((hashtable_hash_t)route_entry_hash,
|
||||
(hashtable_equals_t)route_entry_equals, 16),
|
||||
.net_changes = hashtable_create(
|
||||
(hashtable_hash_t)net_change_hash,
|
||||
(hashtable_equals_t)net_change_equals, 16),
|
||||
.addrs = hashtable_create(
|
||||
.addrs = hashlist_create(
|
||||
(hashtable_hash_t)addr_map_entry_hash,
|
||||
(hashtable_equals_t)addr_map_entry_equals, 16),
|
||||
.vips = hashtable_create((hashtable_hash_t)addr_map_entry_hash,
|
||||
.vips = hashlist_create((hashtable_hash_t)addr_map_entry_hash,
|
||||
(hashtable_equals_t)addr_map_entry_equals, 16),
|
||||
.routes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
|
|
|
@ -323,7 +323,7 @@ struct private_kernel_pfroute_net_t
|
|||
/**
|
||||
* Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
|
||||
*/
|
||||
hashtable_t *addrs;
|
||||
hashlist_t *addrs;
|
||||
|
||||
/**
|
||||
* List of tun devices we installed for virtual IPs
|
||||
|
@ -524,7 +524,7 @@ static void addr_map_entry_add(private_kernel_pfroute_net_t *this,
|
|||
.addr = addr,
|
||||
.iface = iface,
|
||||
);
|
||||
entry = this->addrs->put(this->addrs, entry, entry);
|
||||
entry = this->addrs->ht.put(&this->addrs->ht, entry, entry);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
|
@ -541,7 +541,7 @@ static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
|
|||
.iface = iface,
|
||||
};
|
||||
|
||||
entry = this->addrs->remove(this->addrs, &lookup);
|
||||
entry = this->addrs->ht.remove(&this->addrs->ht, &lookup);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
|
@ -2013,7 +2013,6 @@ METHOD(kernel_net_t, destroy, void,
|
|||
{
|
||||
enumerator_t *enumerator;
|
||||
route_entry_t *route;
|
||||
addr_entry_t *addr;
|
||||
|
||||
enumerator = this->routes->create_enumerator(this->routes);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&route))
|
||||
|
@ -2036,13 +2035,7 @@ METHOD(kernel_net_t, destroy, void,
|
|||
this->net_changes->destroy(this->net_changes);
|
||||
this->net_changes_lock->destroy(this->net_changes_lock);
|
||||
|
||||
enumerator = this->addrs->create_enumerator(this->addrs);
|
||||
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
|
||||
{
|
||||
free(addr);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->addrs->destroy(this->addrs);
|
||||
this->addrs->ht.destroy_function(&this->addrs->ht, (void*)free);
|
||||
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
|
||||
this->tuns->destroy(this->tuns);
|
||||
this->lock->destroy(this->lock);
|
||||
|
@ -2078,7 +2071,7 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
|
|||
},
|
||||
.pid = getpid(),
|
||||
.ifaces = linked_list_create(),
|
||||
.addrs = hashtable_create(
|
||||
.addrs = hashlist_create(
|
||||
(hashtable_hash_t)addr_map_entry_hash,
|
||||
(hashtable_equals_t)addr_map_entry_equals, 16),
|
||||
.routes = hashtable_create((hashtable_hash_t)route_entry_hash,
|
||||
|
|
|
@ -144,6 +144,25 @@ struct private_hashtable_t {
|
|||
#endif
|
||||
};
|
||||
|
||||
typedef struct private_hashlist_t private_hashlist_t;
|
||||
|
||||
/**
|
||||
* Private data of a hashlist_t object.
|
||||
*/
|
||||
struct private_hashlist_t {
|
||||
|
||||
/**
|
||||
* Public part of hash table.
|
||||
*/
|
||||
hashlist_t public;
|
||||
|
||||
/**
|
||||
* Inherited private part of hash table (we get the public part too, but
|
||||
* ignore it).
|
||||
*/
|
||||
private_hashtable_t super;
|
||||
};
|
||||
|
||||
#ifdef HASHTABLE_PROFILER
|
||||
|
||||
#define lookup_start() \
|
||||
|
@ -361,7 +380,7 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
|
|||
while (pair)
|
||||
{
|
||||
lookup_probing();
|
||||
/* when keys are ordered, we compare all items so we can abort earlier
|
||||
/* when keys are sorted, we compare all items so we can abort earlier
|
||||
* even if the hash does not match, but only as long as we don't
|
||||
* have a callback */
|
||||
if (!use_callback && this->cmp)
|
||||
|
@ -445,13 +464,6 @@ METHOD(hashtable_t, get, void*,
|
|||
return pair ? pair->value : NULL;
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, get_match, void*,
|
||||
private_hashtable_t *this, const void *key, hashtable_equals_t match)
|
||||
{
|
||||
pair_t *pair = find_key(this, key, match, NULL, NULL);
|
||||
return pair ? pair->value : NULL;
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, remove_, void*,
|
||||
private_hashtable_t *this, const void *key)
|
||||
{
|
||||
|
@ -595,51 +607,77 @@ static void destroy_internal(private_hashtable_t *this,
|
|||
}
|
||||
}
|
||||
free(this->table);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, destroy, void,
|
||||
private_hashtable_t *this)
|
||||
{
|
||||
destroy_internal(this, NULL);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, destroy_function, void,
|
||||
private_hashtable_t *this, void (*fn)(void*,const void*))
|
||||
{
|
||||
destroy_internal(this, fn);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash table
|
||||
*/
|
||||
static private_hashtable_t *hashtable_create_internal(hashtable_hash_t hash,
|
||||
u_int size)
|
||||
METHOD(hashtable_t, create_enumerator_hashlist, enumerator_t*,
|
||||
private_hashlist_t *this)
|
||||
{
|
||||
private_hashtable_t *this;
|
||||
return create_enumerator(&this->super);
|
||||
}
|
||||
|
||||
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,
|
||||
.destroy_function = _destroy_function,
|
||||
},
|
||||
.hash = hash,
|
||||
);
|
||||
METHOD(hashtable_t, put_hashlist, void*,
|
||||
private_hashlist_t *this, const void *key, void *value)
|
||||
{
|
||||
return put(&this->super, key, value);
|
||||
}
|
||||
|
||||
init_hashtable(this, size);
|
||||
METHOD(hashtable_t, get_hashlist, void*,
|
||||
private_hashlist_t *this, const void *key)
|
||||
{
|
||||
return get(&this->super, key);
|
||||
}
|
||||
|
||||
#ifdef HASHTABLE_PROFILER
|
||||
this->backtrace = backtrace_create(3);
|
||||
#endif
|
||||
METHOD(hashlist_t, get_match, void*,
|
||||
private_hashlist_t *this, const void *key, hashtable_equals_t match)
|
||||
{
|
||||
pair_t *pair = find_key(&this->super, key, match, NULL, NULL);
|
||||
return pair ? pair->value : NULL;
|
||||
}
|
||||
|
||||
return this;
|
||||
METHOD(hashtable_t, remove_hashlist, void*,
|
||||
private_hashlist_t *this, const void *key)
|
||||
{
|
||||
return remove_(&this->super, key);
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, remove_at_hashlist, void,
|
||||
private_hashlist_t *this, private_enumerator_t *enumerator)
|
||||
{
|
||||
remove_at(&this->super, enumerator);
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, get_count_hashlist, u_int,
|
||||
private_hashlist_t *this)
|
||||
{
|
||||
return get_count(&this->super);
|
||||
}
|
||||
|
||||
METHOD2(hashtable_t, hashlist_t, destroy_hashlist, void,
|
||||
private_hashlist_t *this)
|
||||
{
|
||||
destroy_internal(&this->super, NULL);
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(hashtable_t, destroy_function_hashlist, void,
|
||||
private_hashlist_t *this, void (*fn)(void*,const void*))
|
||||
{
|
||||
destroy_internal(&this->super, fn);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -648,9 +686,78 @@ static private_hashtable_t *hashtable_create_internal(hashtable_hash_t hash,
|
|||
hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
||||
u_int size)
|
||||
{
|
||||
private_hashtable_t *this = hashtable_create_internal(hash, size);
|
||||
private_hashtable_t *this;
|
||||
|
||||
this->equals = equals;
|
||||
INIT(this,
|
||||
.public = {
|
||||
.put = _put,
|
||||
.get = _get,
|
||||
.remove = _remove_,
|
||||
.remove_at = (void*)_remove_at,
|
||||
.get_count = _get_count,
|
||||
.create_enumerator = _create_enumerator,
|
||||
.destroy = _destroy,
|
||||
.destroy_function = _destroy_function,
|
||||
},
|
||||
.hash = hash,
|
||||
.equals = equals,
|
||||
);
|
||||
|
||||
init_hashtable(this, size);
|
||||
|
||||
#ifdef HASHTABLE_PROFILER
|
||||
this->backtrace = backtrace_create(3);
|
||||
#endif
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash table
|
||||
*/
|
||||
static private_hashlist_t *hashlist_create_internal(hashtable_hash_t hash,
|
||||
u_int size)
|
||||
{
|
||||
private_hashlist_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.ht = {
|
||||
.put = _put_hashlist,
|
||||
.get = _get_hashlist,
|
||||
.remove = _remove_hashlist,
|
||||
.remove_at = (void*)_remove_at_hashlist,
|
||||
.get_count = _get_count_hashlist,
|
||||
.create_enumerator = _create_enumerator_hashlist,
|
||||
.destroy = _destroy_hashlist,
|
||||
.destroy_function = _destroy_function_hashlist,
|
||||
},
|
||||
.get_match = _get_match,
|
||||
.destroy = _destroy_hashlist,
|
||||
},
|
||||
.super = {
|
||||
.hash = hash,
|
||||
}
|
||||
);
|
||||
|
||||
init_hashtable(&this->super, size);
|
||||
|
||||
#ifdef HASHTABLE_PROFILER
|
||||
this->super.backtrace = backtrace_create(3);
|
||||
#endif
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Described in header
|
||||
*/
|
||||
hashlist_t *hashlist_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
||||
u_int size)
|
||||
{
|
||||
private_hashlist_t *this = hashlist_create_internal(hash, size);
|
||||
|
||||
this->super.equals = equals;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
@ -658,12 +765,12 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
|||
/*
|
||||
* Described in header
|
||||
*/
|
||||
hashtable_t *hashtable_create_sorted(hashtable_hash_t hash,
|
||||
hashtable_cmp_t cmp, u_int size)
|
||||
hashlist_t *hashlist_create_sorted(hashtable_hash_t hash,
|
||||
hashtable_cmp_t cmp, u_int size)
|
||||
{
|
||||
private_hashtable_t *this = hashtable_create_internal(hash, size);
|
||||
private_hashlist_t *this = hashlist_create_internal(hash, size);
|
||||
|
||||
this->cmp = cmp;
|
||||
this->super.cmp = cmp;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <collections/enumerator.h>
|
||||
|
||||
typedef struct hashtable_t hashtable_t;
|
||||
typedef struct hashlist_t hashlist_t;
|
||||
|
||||
/**
|
||||
* Prototype for a function that computes the hash code from the given key.
|
||||
|
@ -124,25 +125,6 @@ struct hashtable_t {
|
|||
*/
|
||||
void *(*get)(hashtable_t *this, const void *key);
|
||||
|
||||
/**
|
||||
* Returns the first value with a matching key, if the hash table contains
|
||||
* such an entry, otherwise NULL is returned.
|
||||
*
|
||||
* Compared to get() the given match function is used to compare the keys
|
||||
* for equality. The hash function does have to be devised specially in
|
||||
* order to make this work if the match function compares keys differently
|
||||
* than the equals/comparison function provided to the constructor.
|
||||
*
|
||||
* This basically allows to enumerate all entries with the same hash value
|
||||
* in their key's order.
|
||||
*
|
||||
* @param key the key to match against
|
||||
* @param match match function to be used when comparing keys
|
||||
* @return the value, NULL if not found
|
||||
*/
|
||||
void *(*get_match)(hashtable_t *this, const void *key,
|
||||
hashtable_equals_t match);
|
||||
|
||||
/**
|
||||
* Removes the value with the given key from the hash table and returns the
|
||||
* removed value (or NULL if no such value existed).
|
||||
|
@ -183,8 +165,52 @@ struct hashtable_t {
|
|||
};
|
||||
|
||||
/**
|
||||
* Creates an empty hash table object. Items in buckets are ordered in
|
||||
* insertion order.
|
||||
* Class implementing a hash table with ordered keys/items and special query
|
||||
* method.
|
||||
*
|
||||
* @note The ordering only pertains to keys/items in the same bucket (with or
|
||||
* without the same hash value), not to the order when enumerating.
|
||||
*
|
||||
* This is intended to be used with hash functions that intentionally return the
|
||||
* same hash value for different keys so multiple items can be retrieved for a
|
||||
* key.
|
||||
*
|
||||
* It's like storing sorted linked lists in a hash table but with less overhead.
|
||||
*/
|
||||
struct hashlist_t {
|
||||
|
||||
/**
|
||||
* Implements the hash table interface.
|
||||
*/
|
||||
hashtable_t ht;
|
||||
|
||||
/**
|
||||
* Returns the first value with a matching key if the hash table contains
|
||||
* such an entry, otherwise NULL is returned.
|
||||
*
|
||||
* Compared to get() the given match function is used to compare the keys
|
||||
* for equality. The hash function does have to be devised specially in
|
||||
* order to make this work if the match function compares keys differently
|
||||
* than the equals/comparison function provided to the constructor.
|
||||
*
|
||||
* This basically allows to enumerate all entries with the same hash value
|
||||
* in their key's order.
|
||||
*
|
||||
* @param key the key to match against
|
||||
* @param match match function to be used when comparing keys
|
||||
* @return the value, NULL if not found
|
||||
*/
|
||||
void *(*get_match)(hashlist_t *this, const void *key,
|
||||
hashtable_equals_t match);
|
||||
|
||||
/**
|
||||
* Destroys a hash list object.
|
||||
*/
|
||||
void (*destroy)(hashlist_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an empty hash table object.
|
||||
*
|
||||
* @param hash hash function
|
||||
* @param equals equals function
|
||||
|
@ -195,15 +221,25 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
|||
u_int size);
|
||||
|
||||
/**
|
||||
* Creates an empty hash table object with keys in each bucket sorted according
|
||||
* to the given comparison function.
|
||||
* Creates an empty hash list object with each bucket's keys in insertion order.
|
||||
*
|
||||
* @param hash hash function
|
||||
* @param equals equals function
|
||||
* @param size initial size
|
||||
* @return hashtable_t object
|
||||
*/
|
||||
hashlist_t *hashlist_create(hashtable_hash_t hash, hashtable_equals_t equals,
|
||||
u_int size);
|
||||
|
||||
/**
|
||||
* Creates an empty hash list object with sorted keys in each bucket.
|
||||
*
|
||||
* @param hash hash function
|
||||
* @param cmp comparison function
|
||||
* @param size initial size
|
||||
* @return hashtable_t object.
|
||||
*/
|
||||
hashtable_t *hashtable_create_sorted(hashtable_hash_t hash,
|
||||
hashtable_cmp_t cmp, u_int size);
|
||||
hashlist_t *hashlist_create_sorted(hashtable_hash_t hash,
|
||||
hashtable_cmp_t cmp, u_int size);
|
||||
|
||||
#endif /** HASHTABLE_H_ @}*/
|
||||
|
|
|
@ -65,7 +65,7 @@ struct private_plugin_loader_t {
|
|||
/**
|
||||
* Hashtable for registered features, as registered_feature_t
|
||||
*/
|
||||
hashtable_t *features;
|
||||
hashlist_t *features;
|
||||
|
||||
/**
|
||||
* Loaded features (stored in reverse order), as provided_feature_t
|
||||
|
@ -911,14 +911,16 @@ static void register_features(private_plugin_loader_t *this,
|
|||
{
|
||||
case FEATURE_PROVIDE:
|
||||
lookup.feature = feature;
|
||||
registered = this->features->get(this->features, &lookup);
|
||||
registered = this->features->ht.get(&this->features->ht,
|
||||
&lookup);
|
||||
if (!registered)
|
||||
{
|
||||
INIT(registered,
|
||||
.feature = feature,
|
||||
.plugins = linked_list_create(),
|
||||
);
|
||||
this->features->put(this->features, registered, registered);
|
||||
this->features->ht.put(&this->features->ht, registered,
|
||||
registered);
|
||||
}
|
||||
INIT(provided,
|
||||
.entry = entry,
|
||||
|
@ -950,13 +952,13 @@ static void unregister_feature(private_plugin_loader_t *this,
|
|||
registered_feature_t *registered, lookup;
|
||||
|
||||
lookup.feature = provided->feature;
|
||||
registered = this->features->get(this->features, &lookup);
|
||||
registered = this->features->ht.get(&this->features->ht, &lookup);
|
||||
if (registered)
|
||||
{
|
||||
registered->plugins->remove(registered->plugins, provided, NULL);
|
||||
if (registered->plugins->get_count(registered->plugins) == 0)
|
||||
{
|
||||
this->features->remove(this->features, &lookup);
|
||||
this->features->ht.remove(&this->features->ht, &lookup);
|
||||
registered->plugins->destroy(registered->plugins);
|
||||
free(registered);
|
||||
}
|
||||
|
@ -1444,7 +1446,7 @@ plugin_loader_t *plugin_loader_create()
|
|||
},
|
||||
.plugins = linked_list_create(),
|
||||
.loaded = linked_list_create(),
|
||||
.features = hashtable_create(
|
||||
.features = hashlist_create(
|
||||
(hashtable_hash_t)registered_feature_hash,
|
||||
(hashtable_equals_t)registered_feature_equals, 64),
|
||||
);
|
||||
|
|
|
@ -19,17 +19,22 @@
|
|||
#include <utils/chunk.h>
|
||||
|
||||
/*******************************************************************************
|
||||
* string hash table functions
|
||||
* hash table functions
|
||||
*/
|
||||
|
||||
static u_int hash(char *key)
|
||||
static u_int hash_match(char *key)
|
||||
{
|
||||
return chunk_hash(chunk_from_str(key));
|
||||
return chunk_hash(chunk_create(key, 4));
|
||||
}
|
||||
|
||||
static bool equals(char *key1, char *key2)
|
||||
static bool equal_match(char *key1, char *key2)
|
||||
{
|
||||
return streq(key1, key2);
|
||||
if (!strneq(key1, key2, 4))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* look for an item with a key < than what we look for */
|
||||
return strcmp(key1, key2) >= 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -38,17 +43,77 @@ static bool equals(char *key1, char *key2)
|
|||
|
||||
static hashtable_t *ht;
|
||||
|
||||
typedef enum {
|
||||
/* regular string hash table */
|
||||
HASHTABLE_REGULAR,
|
||||
/* regular string hash list */
|
||||
HASHLIST_REGULAR,
|
||||
/* sorted string hash list */
|
||||
HASHLIST_REGULAR_SORTED,
|
||||
REGULAR_MAX,
|
||||
/* hash table with only 4 characters hashed -> one bucket tests */
|
||||
HASHTABLE_FUZZY = REGULAR_MAX,
|
||||
/* hash list with only 4 characters hashed */
|
||||
HASHLIST_FUZZY,
|
||||
/* sorted string hash list with only 4 characters hashed */
|
||||
HASHLIST_FUZZY_SORTED,
|
||||
HASHTABLE_MAX,
|
||||
} hashtable_type_t;
|
||||
|
||||
/**
|
||||
* Create a specific hash table/list
|
||||
*/
|
||||
static hashtable_t *create_hashtable(int i)
|
||||
{
|
||||
hashlist_t *hl = NULL;
|
||||
|
||||
DESTROY_IF(ht);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case HASHTABLE_REGULAR:
|
||||
ht = hashtable_create(hashtable_hash_str,
|
||||
hashtable_equals_str, 0);
|
||||
break;
|
||||
case HASHLIST_REGULAR:
|
||||
hl = hashlist_create(hashtable_hash_str,
|
||||
hashtable_equals_str, 0);
|
||||
break;
|
||||
case HASHLIST_REGULAR_SORTED:
|
||||
hl = hashlist_create_sorted(hashtable_hash_str,
|
||||
(hashtable_cmp_t)strcmp, 0);
|
||||
break;
|
||||
case HASHTABLE_FUZZY:
|
||||
ht = hashtable_create((hashtable_hash_t)hash_match,
|
||||
hashtable_equals_str, 0);
|
||||
break;
|
||||
case HASHLIST_FUZZY:
|
||||
hl = hashlist_create((hashtable_hash_t)hash_match,
|
||||
hashtable_equals_str, 0);
|
||||
break;
|
||||
case HASHLIST_FUZZY_SORTED:
|
||||
hl = hashlist_create_sorted((hashtable_hash_t)hash_match,
|
||||
(hashtable_cmp_t)strcmp, 0);
|
||||
break;
|
||||
}
|
||||
if (hl)
|
||||
{
|
||||
ht = &hl->ht;
|
||||
}
|
||||
ck_assert_int_eq(ht->get_count(ht), 0);
|
||||
return ht;
|
||||
}
|
||||
|
||||
START_SETUP(setup_ht)
|
||||
{
|
||||
ht = hashtable_create((hashtable_hash_t)hash,
|
||||
(hashtable_equals_t)equals, 0);
|
||||
ck_assert_int_eq(ht->get_count(ht), 0);
|
||||
create_hashtable(_i);
|
||||
}
|
||||
END_SETUP
|
||||
|
||||
START_TEARDOWN(teardown_ht)
|
||||
{
|
||||
ht->destroy(ht);
|
||||
ht = NULL;
|
||||
}
|
||||
END_TEARDOWN
|
||||
|
||||
|
@ -86,28 +151,13 @@ END_TEST
|
|||
* get_match
|
||||
*/
|
||||
|
||||
static u_int hash_match(char *key)
|
||||
{
|
||||
return chunk_hash(chunk_create(key, 4));
|
||||
}
|
||||
|
||||
static bool equal_match(char *key1, char *key2)
|
||||
{
|
||||
if (!strneq(key1, key2, 4))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* look for an item with a key < than what we look for */
|
||||
return strcmp(key1, key2) >= 0;
|
||||
}
|
||||
|
||||
START_TEST(test_get_match)
|
||||
{
|
||||
hashlist_t *hl;
|
||||
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
|
||||
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
|
||||
|
||||
ht = hashtable_create((hashtable_hash_t)hash_match,
|
||||
(hashtable_equals_t)equals, 0);
|
||||
hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY);
|
||||
|
||||
ht->put(ht, k1, v1);
|
||||
ht->put(ht, k2, v2);
|
||||
|
@ -118,31 +168,31 @@ START_TEST(test_get_match)
|
|||
ck_assert(streq(ht->get(ht, k3), v3));
|
||||
ck_assert(value == NULL);
|
||||
|
||||
value = ht->get_match(ht, k1, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k1, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
value = ht->get_match(ht, k2, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k2, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v2));
|
||||
value = ht->get_match(ht, k3, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k3, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
value = ht->get_match(ht, k4, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k4, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
|
||||
ht->destroy(ht);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_get_match_remove)
|
||||
{
|
||||
hashlist_t *hl;
|
||||
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
|
||||
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
|
||||
|
||||
ht = hashtable_create((hashtable_hash_t)hash_match,
|
||||
(hashtable_equals_t)equals, 0);
|
||||
hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY);
|
||||
|
||||
/* by removing and reinserting the first item we verify that insertion
|
||||
* order is adhered */
|
||||
ht->put(ht, k1, v1);
|
||||
ht->put(ht, k2, v2);
|
||||
ht->put(ht, k3, v3);
|
||||
|
@ -153,31 +203,30 @@ START_TEST(test_get_match_remove)
|
|||
ck_assert(streq(ht->get(ht, k2), v2));
|
||||
ck_assert(streq(ht->get(ht, k3), v3));
|
||||
|
||||
value = ht->get_match(ht, k1, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k1, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
value = ht->get_match(ht, k2, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k2, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v2));
|
||||
value = ht->get_match(ht, k3, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k3, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v3));
|
||||
value = ht->get_match(ht, k4, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k4, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v3));
|
||||
|
||||
ht->destroy(ht);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_get_match_sorted)
|
||||
{
|
||||
hashlist_t *hl;
|
||||
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
|
||||
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
|
||||
|
||||
ht = hashtable_create_sorted((hashtable_hash_t)hash_match,
|
||||
(hashtable_cmp_t)strcmp, 0);
|
||||
hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY_SORTED);
|
||||
|
||||
/* since the keys are sorted, the insertion order doesn't matter */
|
||||
ht->put(ht, k3, v3);
|
||||
ht->put(ht, k2, v2);
|
||||
ht->put(ht, k1, v1);
|
||||
|
@ -190,20 +239,18 @@ START_TEST(test_get_match_sorted)
|
|||
ck_assert(streq(ht->get(ht, k3), v3));
|
||||
ck_assert(streq(ht->get(ht, k4), v1));
|
||||
|
||||
value = ht->get_match(ht, k1, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k1, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
value = ht->get_match(ht, k2, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k2, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v2));
|
||||
value = ht->get_match(ht, k3, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k3, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
value = ht->get_match(ht, k4, (hashtable_equals_t)equal_match);
|
||||
value = hl->get_match(hl, k4, (hashtable_equals_t)equal_match);
|
||||
ck_assert(value != NULL);
|
||||
ck_assert(streq(value, v1));
|
||||
|
||||
ht->destroy(ht);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
@ -252,38 +299,6 @@ START_TEST(test_remove_one_bucket)
|
|||
{
|
||||
char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c";
|
||||
|
||||
ht->destroy(ht);
|
||||
ht = hashtable_create((hashtable_hash_t)hash_match,
|
||||
(hashtable_equals_t)equals, 0);
|
||||
|
||||
do_remove(k1, k2, k3);
|
||||
do_remove(k3, k2, k1);
|
||||
do_remove(k1, k3, k2);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_remove_sorted)
|
||||
{
|
||||
char *k1 = "key1", *k2 = "key2", *k3 = "key3";
|
||||
|
||||
ht->destroy(ht);
|
||||
ht = hashtable_create_sorted((hashtable_hash_t)hash,
|
||||
(hashtable_cmp_t)strcmp, 0);
|
||||
|
||||
do_remove(k1, k2, k3);
|
||||
do_remove(k3, k2, k1);
|
||||
do_remove(k1, k3, k2);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_remove_sorted_one_bucket)
|
||||
{
|
||||
char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c";
|
||||
|
||||
ht->destroy(ht);
|
||||
ht = hashtable_create_sorted((hashtable_hash_t)hash_match,
|
||||
(hashtable_cmp_t)strcmp, 0);
|
||||
|
||||
do_remove(k1, k2, k3);
|
||||
do_remove(k3, k2, k1);
|
||||
do_remove(k1, k3, k2);
|
||||
|
@ -404,15 +419,10 @@ START_TEST(test_remove_at_one_bucket)
|
|||
{
|
||||
char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c";
|
||||
|
||||
ht->destroy(ht);
|
||||
ht = hashtable_create((hashtable_hash_t)hash_match,
|
||||
(hashtable_equals_t)equals, 0);
|
||||
|
||||
do_remove_at(k1, k2, k3);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* many items
|
||||
*/
|
||||
|
@ -432,34 +442,63 @@ static int cmp_int(int *key1, int *key2)
|
|||
return *key1 - *key2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a specific hash table with integers as keys.
|
||||
*/
|
||||
static hashtable_t *create_int_hashtable(int i)
|
||||
{
|
||||
hashlist_t *hl = NULL;
|
||||
|
||||
DESTROY_IF(ht);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case HASHTABLE_REGULAR:
|
||||
ht = hashtable_create((hashtable_hash_t)hash_int,
|
||||
(hashtable_equals_t)equals_int, 0);
|
||||
break;
|
||||
case HASHLIST_REGULAR:
|
||||
hl = hashlist_create((hashtable_hash_t)hash_int,
|
||||
(hashtable_equals_t)equals_int, 0);
|
||||
break;
|
||||
case HASHLIST_REGULAR_SORTED:
|
||||
hl = hashlist_create_sorted((hashtable_hash_t)hash_int,
|
||||
(hashtable_cmp_t)cmp_int, 0);
|
||||
break;
|
||||
}
|
||||
if (hl)
|
||||
{
|
||||
ht = &hl->ht;
|
||||
}
|
||||
ck_assert_int_eq(ht->get_count(ht), 0);
|
||||
return ht;
|
||||
}
|
||||
|
||||
START_SETUP(setup_ht_many)
|
||||
{
|
||||
ht = hashtable_create((hashtable_hash_t)hash_int,
|
||||
(hashtable_equals_t)equals_int, 0);
|
||||
ck_assert_int_eq(ht->get_count(ht), 0);
|
||||
create_int_hashtable(_i >> 1);
|
||||
}
|
||||
END_SETUP
|
||||
|
||||
START_SETUP(setup_ht_many_cmp)
|
||||
START_SETUP(setup_ht_lookups)
|
||||
{
|
||||
ht = hashtable_create_sorted((hashtable_hash_t)hash_int,
|
||||
(hashtable_cmp_t)cmp_int, 0);
|
||||
ck_assert_int_eq(ht->get_count(ht), 0);
|
||||
create_int_hashtable(_i);
|
||||
}
|
||||
END_SETUP
|
||||
|
||||
START_TEARDOWN(teardown_ht_many)
|
||||
{
|
||||
ht->destroy_function(ht, (void*)free);
|
||||
ht = NULL;
|
||||
}
|
||||
END_TEARDOWN
|
||||
|
||||
START_TEST(test_many_items)
|
||||
{
|
||||
u_int count = 250000;
|
||||
u_int count = 100000;
|
||||
int i, *val, r;
|
||||
|
||||
#define GET_VALUE(i) ({ _i == 0 ? i : (count-1-i); })
|
||||
#define GET_VALUE(i) ({ (_i % 2) == 0 ? i : (count-1-i); })
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
|
@ -587,10 +626,11 @@ Suite *hashtable_suite_create()
|
|||
|
||||
tc = tcase_create("put/get");
|
||||
tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
|
||||
tcase_add_test(tc, test_put_get);
|
||||
tcase_add_loop_test(tc, test_put_get, 0, HASHTABLE_MAX);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("get_match");
|
||||
tcase_add_checked_fixture(tc, NULL, teardown_ht);
|
||||
tcase_add_test(tc, test_get_match);
|
||||
tcase_add_test(tc, test_get_match_remove);
|
||||
tcase_add_test(tc, test_get_match_sorted);
|
||||
|
@ -598,39 +638,32 @@ Suite *hashtable_suite_create()
|
|||
|
||||
tc = tcase_create("remove");
|
||||
tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
|
||||
tcase_add_test(tc, test_remove);
|
||||
tcase_add_test(tc, test_remove_one_bucket);
|
||||
tcase_add_test(tc, test_remove_sorted);
|
||||
tcase_add_test(tc, test_remove_sorted_one_bucket);
|
||||
tcase_add_loop_test(tc, test_remove, 0, REGULAR_MAX);
|
||||
tcase_add_loop_test(tc, test_remove_one_bucket, HASHTABLE_FUZZY, HASHTABLE_MAX);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("enumerator");
|
||||
tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
|
||||
tcase_add_test(tc, test_enumerator);
|
||||
tcase_add_loop_test(tc, test_enumerator, 0, HASHTABLE_MAX);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("remove_at");
|
||||
tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
|
||||
tcase_add_test(tc, test_remove_at);
|
||||
tcase_add_test(tc, test_remove_at_one_bucket);
|
||||
tcase_add_loop_test(tc, test_remove_at, 0, REGULAR_MAX);
|
||||
tcase_add_loop_test(tc, test_remove_at_one_bucket, HASHTABLE_FUZZY, HASHTABLE_MAX);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("many items");
|
||||
tcase_add_checked_fixture(tc, setup_ht_many, teardown_ht_many);
|
||||
tcase_set_timeout(tc, 10);
|
||||
tcase_add_loop_test(tc, test_many_items, 0, 2);
|
||||
tcase_add_test(tc, test_many_lookups_success);
|
||||
tcase_add_test(tc, test_many_lookups_failure_larger);
|
||||
tcase_add_test(tc, test_many_lookups_failure_smaller);
|
||||
tcase_add_loop_test(tc, test_many_items, 0, REGULAR_MAX << 1);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("many items sorted");
|
||||
tcase_add_checked_fixture(tc, setup_ht_many_cmp, teardown_ht_many);
|
||||
tcase_set_timeout(tc, 10);
|
||||
tcase_add_loop_test(tc, test_many_items, 0, 2);
|
||||
tcase_add_test(tc, test_many_lookups_success);
|
||||
tcase_add_test(tc, test_many_lookups_failure_larger);
|
||||
tcase_add_test(tc, test_many_lookups_failure_smaller);
|
||||
tc = tcase_create("many lookups");
|
||||
tcase_add_checked_fixture(tc, setup_ht_lookups, teardown_ht_many);
|
||||
tcase_add_loop_test(tc, test_many_lookups_success, 0, REGULAR_MAX);
|
||||
tcase_add_loop_test(tc, test_many_lookups_failure_larger, 0, REGULAR_MAX);
|
||||
tcase_add_loop_test(tc, test_many_lookups_failure_smaller, 0, REGULAR_MAX);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
|
|
Loading…
Reference in New Issue