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:
Tobias Brunner
2020-07-20 13:50:11 +02:00
parent 4334f61284
commit d9944102f5
6 changed files with 386 additions and 224 deletions
@@ -330,12 +330,12 @@ struct private_kernel_netlink_net_t {
/** /**
* Map for IP addresses to iface_entry_t objects (addr_map_entry_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) * Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t)
*/ */
hashtable_t *vips; hashlist_t *vips;
/** /**
* netlink rt socket (routing) * netlink rt socket (routing)
@@ -375,7 +375,7 @@ struct private_kernel_netlink_net_t {
/** /**
* installed routes * installed routes
*/ */
hashtable_t *routes; hashlist_t *routes;
/** /**
* mutex for 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->net_changes_lock->lock(this->net_changes_lock);
this->routes_lock->lock(this->routes_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)) while (enumerator->enumerate(enumerator, NULL, (void**)&route))
{ {
net_change_t *change, lookup = { 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 * 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) iface_entry_t *iface)
{ {
addr_map_entry_t *entry; addr_map_entry_t *entry;
@@ -627,14 +627,14 @@ static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr,
.addr = addr, .addr = addr,
.iface = iface, .iface = iface,
); );
entry = map->put(map, entry, entry); entry = map->ht.put(&map->ht, entry, entry);
free(entry); free(entry);
} }
/** /**
* Remove an address map 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) iface_entry_t *iface)
{ {
addr_map_entry_t *entry, lookup = { 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, .iface = iface,
}; };
entry = map->remove(map, &lookup); entry = map->ht.remove(&map->ht, &lookup);
free(entry); free(entry);
} }
@@ -1241,7 +1241,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
}; };
addr_entry_t *addr; addr_entry_t *addr;
entry = this->vips->get(this->vips, &lookup); entry = this->vips->ht.get(&this->vips->ht, &lookup);
if (entry) if (entry)
{ {
if (hdr->nlmsg_type == RTM_NEWADDR) if (hdr->nlmsg_type == RTM_NEWADDR)
@@ -1261,7 +1261,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
host->destroy(host); host->destroy(host);
return; return;
} }
entry = this->addrs->get(this->addrs, &lookup); entry = this->addrs->ht.get(&this->addrs->ht, &lookup);
if (entry) if (entry)
{ {
if (hdr->nlmsg_type == RTM_DELADDR) 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); 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) if (found)
{ {
this->routes_lock->unlock(this->routes_lock); this->routes_lock->unlock(this->routes_lock);
@@ -2755,7 +2755,7 @@ METHOD(kernel_net_t, add_route, status_t,
if (status == SUCCESS) if (status == SUCCESS)
{ {
found = route_entry_clone(&lookup.route); 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); this->routes_lock->unlock(this->routes_lock);
return status; return status;
@@ -2785,7 +2785,7 @@ METHOD(kernel_net_t, del_route, status_t,
} }
this->routes_lock->lock(this->routes_lock); 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) if (!found)
{ {
this->routes_lock->unlock(this->routes_lock); 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 * 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; map->ht.destroy_function(&map->ht, (void*)free);
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);
} }
METHOD(kernel_net_t, destroy, void, METHOD(kernel_net_t, destroy, void,
@@ -3055,7 +3046,7 @@ METHOD(kernel_net_t, destroy, void,
lib->watcher->remove(lib->watcher, this->socket_events); lib->watcher->remove(lib->watcher, this->socket_events);
close(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)) while (enumerator->enumerate(enumerator, NULL, (void**)&route))
{ {
manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen, 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, lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-netlink.parallel_route", FALSE, lib->ns)), "%s.plugins.kernel-netlink.parallel_route", FALSE, lib->ns)),
.rt_exclude = linked_list_create(), .rt_exclude = linked_list_create(),
.routes = hashtable_create((hashtable_hash_t)route_entry_hash, .routes = hashlist_create((hashtable_hash_t)route_entry_hash,
(hashtable_equals_t)route_entry_equals, 16), (hashtable_equals_t)route_entry_equals, 16),
.net_changes = hashtable_create( .net_changes = hashtable_create(
(hashtable_hash_t)net_change_hash, (hashtable_hash_t)net_change_hash,
(hashtable_equals_t)net_change_equals, 16), (hashtable_equals_t)net_change_equals, 16),
.addrs = hashtable_create( .addrs = hashlist_create(
(hashtable_hash_t)addr_map_entry_hash, (hashtable_hash_t)addr_map_entry_hash,
(hashtable_equals_t)addr_map_entry_equals, 16), (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), (hashtable_equals_t)addr_map_entry_equals, 16),
.routes_lock = mutex_create(MUTEX_TYPE_DEFAULT), .routes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
.net_changes_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) * 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 * 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, .addr = addr,
.iface = iface, .iface = iface,
); );
entry = this->addrs->put(this->addrs, entry, entry); entry = this->addrs->ht.put(&this->addrs->ht, entry, entry);
free(entry); free(entry);
} }
@@ -541,7 +541,7 @@ static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
.iface = iface, .iface = iface,
}; };
entry = this->addrs->remove(this->addrs, &lookup); entry = this->addrs->ht.remove(&this->addrs->ht, &lookup);
free(entry); free(entry);
} }
@@ -2013,7 +2013,6 @@ METHOD(kernel_net_t, destroy, void,
{ {
enumerator_t *enumerator; enumerator_t *enumerator;
route_entry_t *route; route_entry_t *route;
addr_entry_t *addr;
enumerator = this->routes->create_enumerator(this->routes); enumerator = this->routes->create_enumerator(this->routes);
while (enumerator->enumerate(enumerator, NULL, (void**)&route)) 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->destroy(this->net_changes);
this->net_changes_lock->destroy(this->net_changes_lock); this->net_changes_lock->destroy(this->net_changes_lock);
enumerator = this->addrs->create_enumerator(this->addrs); this->addrs->ht.destroy_function(&this->addrs->ht, (void*)free);
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
{
free(addr);
}
enumerator->destroy(enumerator);
this->addrs->destroy(this->addrs);
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
this->tuns->destroy(this->tuns); this->tuns->destroy(this->tuns);
this->lock->destroy(this->lock); this->lock->destroy(this->lock);
@@ -2078,7 +2071,7 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
}, },
.pid = getpid(), .pid = getpid(),
.ifaces = linked_list_create(), .ifaces = linked_list_create(),
.addrs = hashtable_create( .addrs = hashlist_create(
(hashtable_hash_t)addr_map_entry_hash, (hashtable_hash_t)addr_map_entry_hash,
(hashtable_equals_t)addr_map_entry_equals, 16), (hashtable_equals_t)addr_map_entry_equals, 16),
.routes = hashtable_create((hashtable_hash_t)route_entry_hash, .routes = hashtable_create((hashtable_hash_t)route_entry_hash,
+147 -40
View File
@@ -144,6 +144,25 @@ struct private_hashtable_t {
#endif #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 #ifdef HASHTABLE_PROFILER
#define lookup_start() \ #define lookup_start() \
@@ -361,7 +380,7 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
while (pair) while (pair)
{ {
lookup_probing(); 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 * even if the hash does not match, but only as long as we don't
* have a callback */ * have a callback */
if (!use_callback && this->cmp) if (!use_callback && this->cmp)
@@ -445,13 +464,6 @@ METHOD(hashtable_t, get, void*,
return pair ? pair->value : NULL; 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*, METHOD(hashtable_t, remove_, void*,
private_hashtable_t *this, const void *key) private_hashtable_t *this, const void *key)
{ {
@@ -595,51 +607,77 @@ static void destroy_internal(private_hashtable_t *this,
} }
} }
free(this->table); free(this->table);
free(this);
} }
METHOD(hashtable_t, destroy, void, METHOD(hashtable_t, destroy, void,
private_hashtable_t *this) private_hashtable_t *this)
{ {
destroy_internal(this, NULL); destroy_internal(this, NULL);
free(this);
} }
METHOD(hashtable_t, destroy_function, void, METHOD(hashtable_t, destroy_function, void,
private_hashtable_t *this, void (*fn)(void*,const void*)) private_hashtable_t *this, void (*fn)(void*,const void*))
{ {
destroy_internal(this, fn); destroy_internal(this, fn);
free(this);
} }
/** METHOD(hashtable_t, create_enumerator_hashlist, enumerator_t*,
* Create a hash table private_hashlist_t *this)
*/
static private_hashtable_t *hashtable_create_internal(hashtable_hash_t hash,
u_int size)
{ {
private_hashtable_t *this; return create_enumerator(&this->super);
}
INIT(this, METHOD(hashtable_t, put_hashlist, void*,
.public = { private_hashlist_t *this, const void *key, void *value)
.put = _put, {
.get = _get, return put(&this->super, key, value);
.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,
);
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 METHOD(hashlist_t, get_match, void*,
this->backtrace = backtrace_create(3); private_hashlist_t *this, const void *key, hashtable_equals_t match)
#endif {
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, hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
u_int size) 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; return &this->public;
} }
@@ -658,12 +765,12 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
/* /*
* Described in header * Described in header
*/ */
hashtable_t *hashtable_create_sorted(hashtable_hash_t hash, hashlist_t *hashlist_create_sorted(hashtable_hash_t hash,
hashtable_cmp_t cmp, u_int size) 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; return &this->public;
} }
+61 -25
View File
@@ -24,6 +24,7 @@
#include <collections/enumerator.h> #include <collections/enumerator.h>
typedef struct hashtable_t hashtable_t; 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. * 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); 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 * Removes the value with the given key from the hash table and returns the
* removed value (or NULL if no such value existed). * 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 * Class implementing a hash table with ordered keys/items and special query
* insertion order. * 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 hash hash function
* @param equals equals function * @param equals equals function
@@ -195,15 +221,25 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
u_int size); u_int size);
/** /**
* Creates an empty hash table object with keys in each bucket sorted according * Creates an empty hash list object with each bucket's keys in insertion order.
* to the given comparison function. *
* @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 hash hash function
* @param cmp comparison function * @param cmp comparison function
* @param size initial size * @param size initial size
* @return hashtable_t object. * @return hashtable_t object.
*/ */
hashtable_t *hashtable_create_sorted(hashtable_hash_t hash, hashlist_t *hashlist_create_sorted(hashtable_hash_t hash,
hashtable_cmp_t cmp, u_int size); hashtable_cmp_t cmp, u_int size);
#endif /** HASHTABLE_H_ @}*/ #endif /** HASHTABLE_H_ @}*/
+8 -6
View File
@@ -65,7 +65,7 @@ struct private_plugin_loader_t {
/** /**
* Hashtable for registered features, as registered_feature_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 * 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: case FEATURE_PROVIDE:
lookup.feature = feature; lookup.feature = feature;
registered = this->features->get(this->features, &lookup); registered = this->features->ht.get(&this->features->ht,
&lookup);
if (!registered) if (!registered)
{ {
INIT(registered, INIT(registered,
.feature = feature, .feature = feature,
.plugins = linked_list_create(), .plugins = linked_list_create(),
); );
this->features->put(this->features, registered, registered); this->features->ht.put(&this->features->ht, registered,
registered);
} }
INIT(provided, INIT(provided,
.entry = entry, .entry = entry,
@@ -950,13 +952,13 @@ static void unregister_feature(private_plugin_loader_t *this,
registered_feature_t *registered, lookup; registered_feature_t *registered, lookup;
lookup.feature = provided->feature; lookup.feature = provided->feature;
registered = this->features->get(this->features, &lookup); registered = this->features->ht.get(&this->features->ht, &lookup);
if (registered) if (registered)
{ {
registered->plugins->remove(registered->plugins, provided, NULL); registered->plugins->remove(registered->plugins, provided, NULL);
if (registered->plugins->get_count(registered->plugins) == 0) 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); registered->plugins->destroy(registered->plugins);
free(registered); free(registered);
} }
@@ -1444,7 +1446,7 @@ plugin_loader_t *plugin_loader_create()
}, },
.plugins = linked_list_create(), .plugins = linked_list_create(),
.loaded = linked_list_create(), .loaded = linked_list_create(),
.features = hashtable_create( .features = hashlist_create(
(hashtable_hash_t)registered_feature_hash, (hashtable_hash_t)registered_feature_hash,
(hashtable_equals_t)registered_feature_equals, 64), (hashtable_equals_t)registered_feature_equals, 64),
); );
+145 -112
View File
@@ -19,17 +19,22 @@
#include <utils/chunk.h> #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; 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) START_SETUP(setup_ht)
{ {
ht = hashtable_create((hashtable_hash_t)hash, create_hashtable(_i);
(hashtable_equals_t)equals, 0);
ck_assert_int_eq(ht->get_count(ht), 0);
} }
END_SETUP END_SETUP
START_TEARDOWN(teardown_ht) START_TEARDOWN(teardown_ht)
{ {
ht->destroy(ht); ht->destroy(ht);
ht = NULL;
} }
END_TEARDOWN END_TEARDOWN
@@ -86,28 +151,13 @@ END_TEST
* get_match * 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) START_TEST(test_get_match)
{ {
hashlist_t *hl;
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c"; char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value; char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
ht = hashtable_create((hashtable_hash_t)hash_match, hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY);
(hashtable_equals_t)equals, 0);
ht->put(ht, k1, v1); ht->put(ht, k1, v1);
ht->put(ht, k2, v2); ht->put(ht, k2, v2);
@@ -118,31 +168,31 @@ START_TEST(test_get_match)
ck_assert(streq(ht->get(ht, k3), v3)); ck_assert(streq(ht->get(ht, k3), v3));
ck_assert(value == NULL); 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(value != NULL);
ck_assert(streq(value, v1)); 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(value != NULL);
ck_assert(streq(value, v2)); 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(value != NULL);
ck_assert(streq(value, v1)); 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(value != NULL);
ck_assert(streq(value, v1)); ck_assert(streq(value, v1));
ht->destroy(ht);
} }
END_TEST END_TEST
START_TEST(test_get_match_remove) START_TEST(test_get_match_remove)
{ {
hashlist_t *hl;
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c"; char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value; char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
ht = hashtable_create((hashtable_hash_t)hash_match, hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY);
(hashtable_equals_t)equals, 0);
/* by removing and reinserting the first item we verify that insertion
* order is adhered */
ht->put(ht, k1, v1); ht->put(ht, k1, v1);
ht->put(ht, k2, v2); ht->put(ht, k2, v2);
ht->put(ht, k3, v3); 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, k2), v2));
ck_assert(streq(ht->get(ht, k3), v3)); 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(value != NULL);
ck_assert(streq(value, v1)); 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(value != NULL);
ck_assert(streq(value, v2)); 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(value != NULL);
ck_assert(streq(value, v3)); 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(value != NULL);
ck_assert(streq(value, v3)); ck_assert(streq(value, v3));
ht->destroy(ht);
} }
END_TEST END_TEST
START_TEST(test_get_match_sorted) START_TEST(test_get_match_sorted)
{ {
hashlist_t *hl;
char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c"; char *k1 = "key1_a", *k2 = "key2", *k3 = "key1_b", *k4 = "key1_c";
char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value; char *v1 = "val1", *v2 = "val2", *v3 = "val3", *value;
ht = hashtable_create_sorted((hashtable_hash_t)hash_match, hl = (hashlist_t*)create_hashtable(HASHLIST_FUZZY_SORTED);
(hashtable_cmp_t)strcmp, 0);
/* since the keys are sorted, the insertion order doesn't matter */
ht->put(ht, k3, v3); ht->put(ht, k3, v3);
ht->put(ht, k2, v2); ht->put(ht, k2, v2);
ht->put(ht, k1, v1); 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, k3), v3));
ck_assert(streq(ht->get(ht, k4), v1)); 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(value != NULL);
ck_assert(streq(value, v1)); 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(value != NULL);
ck_assert(streq(value, v2)); 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(value != NULL);
ck_assert(streq(value, v1)); 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(value != NULL);
ck_assert(streq(value, v1)); ck_assert(streq(value, v1));
ht->destroy(ht);
} }
END_TEST END_TEST
@@ -252,38 +299,6 @@ START_TEST(test_remove_one_bucket)
{ {
char *k1 = "key1_a", *k2 = "key1_b", *k3 = "key1_c"; 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(k1, k2, k3);
do_remove(k3, k2, k1); do_remove(k3, k2, k1);
do_remove(k1, k3, k2); 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"; 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); do_remove_at(k1, k2, k3);
} }
END_TEST END_TEST
/******************************************************************************* /*******************************************************************************
* many items * many items
*/ */
@@ -432,34 +442,63 @@ static int cmp_int(int *key1, int *key2)
return *key1 - *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) START_SETUP(setup_ht_many)
{ {
ht = hashtable_create((hashtable_hash_t)hash_int, create_int_hashtable(_i >> 1);
(hashtable_equals_t)equals_int, 0);
ck_assert_int_eq(ht->get_count(ht), 0);
} }
END_SETUP END_SETUP
START_SETUP(setup_ht_many_cmp) START_SETUP(setup_ht_lookups)
{ {
ht = hashtable_create_sorted((hashtable_hash_t)hash_int, create_int_hashtable(_i);
(hashtable_cmp_t)cmp_int, 0);
ck_assert_int_eq(ht->get_count(ht), 0);
} }
END_SETUP END_SETUP
START_TEARDOWN(teardown_ht_many) START_TEARDOWN(teardown_ht_many)
{ {
ht->destroy_function(ht, (void*)free); ht->destroy_function(ht, (void*)free);
ht = NULL;
} }
END_TEARDOWN END_TEARDOWN
START_TEST(test_many_items) START_TEST(test_many_items)
{ {
u_int count = 250000; u_int count = 100000;
int i, *val, r; 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++) for (i = 0; i < count; i++)
{ {
@@ -587,10 +626,11 @@ Suite *hashtable_suite_create()
tc = tcase_create("put/get"); tc = tcase_create("put/get");
tcase_add_checked_fixture(tc, setup_ht, teardown_ht); 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); suite_add_tcase(s, tc);
tc = tcase_create("get_match"); 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);
tcase_add_test(tc, test_get_match_remove); tcase_add_test(tc, test_get_match_remove);
tcase_add_test(tc, test_get_match_sorted); tcase_add_test(tc, test_get_match_sorted);
@@ -598,39 +638,32 @@ Suite *hashtable_suite_create()
tc = tcase_create("remove"); tc = tcase_create("remove");
tcase_add_checked_fixture(tc, setup_ht, teardown_ht); tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
tcase_add_test(tc, test_remove); tcase_add_loop_test(tc, test_remove, 0, REGULAR_MAX);
tcase_add_test(tc, test_remove_one_bucket); tcase_add_loop_test(tc, test_remove_one_bucket, HASHTABLE_FUZZY, HASHTABLE_MAX);
tcase_add_test(tc, test_remove_sorted);
tcase_add_test(tc, test_remove_sorted_one_bucket);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("enumerator"); tc = tcase_create("enumerator");
tcase_add_checked_fixture(tc, setup_ht, teardown_ht); 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); suite_add_tcase(s, tc);
tc = tcase_create("remove_at"); tc = tcase_create("remove_at");
tcase_add_checked_fixture(tc, setup_ht, teardown_ht); tcase_add_checked_fixture(tc, setup_ht, teardown_ht);
tcase_add_test(tc, test_remove_at); tcase_add_loop_test(tc, test_remove_at, 0, REGULAR_MAX);
tcase_add_test(tc, test_remove_at_one_bucket); tcase_add_loop_test(tc, test_remove_at_one_bucket, HASHTABLE_FUZZY, HASHTABLE_MAX);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("many items"); tc = tcase_create("many items");
tcase_add_checked_fixture(tc, setup_ht_many, teardown_ht_many); tcase_add_checked_fixture(tc, setup_ht_many, teardown_ht_many);
tcase_set_timeout(tc, 10); tcase_set_timeout(tc, 10);
tcase_add_loop_test(tc, test_many_items, 0, 2); tcase_add_loop_test(tc, test_many_items, 0, REGULAR_MAX << 1);
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);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("many items sorted"); tc = tcase_create("many lookups");
tcase_add_checked_fixture(tc, setup_ht_many_cmp, teardown_ht_many); tcase_add_checked_fixture(tc, setup_ht_lookups, teardown_ht_many);
tcase_set_timeout(tc, 10); tcase_add_loop_test(tc, test_many_lookups_success, 0, REGULAR_MAX);
tcase_add_loop_test(tc, test_many_items, 0, 2); tcase_add_loop_test(tc, test_many_lookups_failure_larger, 0, REGULAR_MAX);
tcase_add_test(tc, test_many_lookups_success); tcase_add_loop_test(tc, test_many_lookups_failure_smaller, 0, REGULAR_MAX);
tcase_add_test(tc, test_many_lookups_failure_larger);
tcase_add_test(tc, test_many_lookups_failure_smaller);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
return s; return s;