[glib: 1/2] gnetworkmonitorbase: Use a hash table for storing networks
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 1/2] gnetworkmonitorbase: Use a hash table for storing networks
- Date: Wed, 22 Jan 2020 16:48:24 +0000 (UTC)
commit 4a488ced5d08fb3e28e92b022c407f4669d97029
Author: Philip Withnall <withnall endlessm com>
Date: Fri Jan 10 12:34:45 2020 +0000
gnetworkmonitorbase: Use a hash table for storing networks
Rather than using an array, which requires a lot of iteration over it to
check whether a particular network is present. Using a hash table only
requires iteration in the can_reach() case, where we need to match a
mask in the networks array, rather than equal it.
This should improve performance for large numbers of routes.
Signed-off-by: Philip Withnall <withnall endlessm com>
Fixes: #1925
gio/gnetworkmonitorbase.c | 128 ++++++++++++++++++++++++++++++++--------------
1 file changed, 89 insertions(+), 39 deletions(-)
---
diff --git a/gio/gnetworkmonitorbase.c b/gio/gnetworkmonitorbase.c
index 6ac37c490..be6ec7277 100644
--- a/gio/gnetworkmonitorbase.c
+++ b/gio/gnetworkmonitorbase.c
@@ -45,7 +45,7 @@ enum
struct _GNetworkMonitorBasePrivate
{
- GPtrArray *networks;
+ GHashTable *networks /* (element-type GInetAddressMask) (owned) */;
gboolean have_ipv4_default_route;
gboolean have_ipv6_default_route;
gboolean is_available;
@@ -58,6 +58,9 @@ struct _GNetworkMonitorBasePrivate
static guint network_changed_signal = 0;
static void queue_network_changed (GNetworkMonitorBase *monitor);
+static guint inet_address_mask_hash (gconstpointer key);
+static gboolean inet_address_mask_equal (gconstpointer a,
+ gconstpointer b);
G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorBase, g_network_monitor_base, G_TYPE_OBJECT,
G_ADD_PRIVATE (GNetworkMonitorBase)
@@ -75,7 +78,9 @@ static void
g_network_monitor_base_init (GNetworkMonitorBase *monitor)
{
monitor->priv = g_network_monitor_base_get_instance_private (monitor);
- monitor->priv->networks = g_ptr_array_new_with_free_func (g_object_unref);
+ monitor->priv->networks = g_hash_table_new_full (inet_address_mask_hash,
+ inet_address_mask_equal,
+ g_object_unref, NULL);
monitor->priv->context = g_main_context_get_thread_default ();
if (monitor->priv->context)
g_main_context_ref (monitor->priv->context);
@@ -149,7 +154,7 @@ g_network_monitor_base_finalize (GObject *object)
{
GNetworkMonitorBase *monitor = G_NETWORK_MONITOR_BASE (object);
- g_ptr_array_free (monitor->priv->networks, TRUE);
+ g_hash_table_unref (monitor->priv->networks);
if (monitor->priv->network_changed_source)
{
g_source_destroy (monitor->priv->network_changed_source);
@@ -180,15 +185,18 @@ g_network_monitor_base_can_reach_sockaddr (GNetworkMonitorBase *base,
GSocketAddress *sockaddr)
{
GInetAddress *iaddr;
- int i;
+ GHashTableIter iter;
+ gpointer key;
if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
return FALSE;
iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sockaddr));
- for (i = 0; i < base->priv->networks->len; i++)
+ g_hash_table_iter_init (&iter, base->priv->networks);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
{
- if (g_inet_address_mask_matches (base->priv->networks->pdata[i], iaddr))
+ GInetAddressMask *mask = key;
+ if (g_inet_address_mask_matches (mask, iaddr))
return TRUE;
}
@@ -205,7 +213,7 @@ g_network_monitor_base_can_reach (GNetworkMonitor *monitor,
GSocketAddressEnumerator *enumerator;
GSocketAddress *addr;
- if (base->priv->networks->len == 0)
+ if (g_hash_table_size (base->priv->networks) == 0)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
_("Network unreachable"));
@@ -309,7 +317,7 @@ g_network_monitor_base_can_reach_async (GNetworkMonitor *monitor,
task = g_task_new (monitor, cancellable, callback, user_data);
g_task_set_source_tag (task, g_network_monitor_base_can_reach_async);
- if (G_NETWORK_MONITOR_BASE (monitor)->priv->networks->len == 0)
+ if (g_hash_table_size (G_NETWORK_MONITOR_BASE (monitor)->priv->networks) == 0)
{
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE,
_("Network unreachable"));
@@ -361,6 +369,60 @@ g_network_monitor_base_initable_iface_init (GInitableIface *iface)
iface->init = g_network_monitor_base_initable_init;
}
+static guint
+inet_address_mask_hash (gconstpointer key)
+{
+ GInetAddressMask *mask = G_INET_ADDRESS_MASK (key);
+ guint addr_hash;
+ guint mask_length = g_inet_address_mask_get_length (mask);
+ GInetAddress *addr = g_inet_address_mask_get_address (mask);
+ const guint8 *bytes = g_inet_address_to_bytes (addr);
+ gsize bytes_length = g_inet_address_get_native_size (addr);
+
+ union
+ {
+ const guint8 *bytes;
+ guint32 *hash32;
+ guint64 *hash64;
+ } integerifier;
+
+ /* If we can fit the entire address into the hash key, do it. Don’t worry
+ * about endianness; the address should always be in network endianness. */
+ if (bytes_length == sizeof (guint32))
+ {
+ integerifier.bytes = bytes;
+ addr_hash = *integerifier.hash32;
+ }
+ else if (bytes_length == sizeof (guint64))
+ {
+ integerifier.bytes = bytes;
+ addr_hash = *integerifier.hash64;
+ }
+ else
+ {
+ gsize i;
+
+ /* Otherwise, fall back to adding the bytes together. We do this, rather
+ * than XORing them, as routes often have repeated tuples which would
+ * cancel out under XOR. */
+ addr_hash = 0;
+ for (i = 0; i < bytes_length; i++)
+ addr_hash += bytes[i];
+ }
+
+ return addr_hash + mask_length;;
+}
+
+static gboolean
+inet_address_mask_equal (gconstpointer a,
+ gconstpointer b)
+{
+ GInetAddressMask *mask_a = G_INET_ADDRESS_MASK (a);
+ GInetAddressMask *mask_b = G_INET_ADDRESS_MASK (b);
+
+ return g_inet_address_mask_equal (mask_a, mask_b);
+}
+
static gboolean
emit_network_changed (gpointer user_data)
{
@@ -424,7 +486,7 @@ queue_network_changed (GNetworkMonitorBase *monitor)
/**
* g_network_monitor_base_add_network:
* @monitor: the #GNetworkMonitorBase
- * @network: a #GInetAddressMask
+ * @network: (transfer none): a #GInetAddressMask
*
* Adds @network to @monitor's list of available networks.
*
@@ -434,15 +496,11 @@ void
g_network_monitor_base_add_network (GNetworkMonitorBase *monitor,
GInetAddressMask *network)
{
- int i;
+ if (!g_hash_table_add (monitor->priv->networks, network))
+ return;
- for (i = 0; i < monitor->priv->networks->len; i++)
- {
- if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
- return;
- }
+ g_object_ref (network); /* for the element now stored in the hash table */
- g_ptr_array_add (monitor->priv->networks, g_object_ref (network));
if (g_inet_address_mask_get_length (network) == 0)
{
switch (g_inet_address_mask_get_family (network))
@@ -481,33 +539,25 @@ void
g_network_monitor_base_remove_network (GNetworkMonitorBase *monitor,
GInetAddressMask *network)
{
- int i;
+ if (!g_hash_table_remove (monitor->priv->networks, network))
+ return;
- for (i = 0; i < monitor->priv->networks->len; i++)
+ if (g_inet_address_mask_get_length (network) == 0)
{
- if (g_inet_address_mask_equal (monitor->priv->networks->pdata[i], network))
+ switch (g_inet_address_mask_get_family (network))
{
- g_ptr_array_remove_index_fast (monitor->priv->networks, i);
-
- if (g_inet_address_mask_get_length (network) == 0)
- {
- switch (g_inet_address_mask_get_family (network))
- {
- case G_SOCKET_FAMILY_IPV4:
- monitor->priv->have_ipv4_default_route = FALSE;
- break;
- case G_SOCKET_FAMILY_IPV6:
- monitor->priv->have_ipv6_default_route = FALSE;
- break;
- default:
- break;
- }
- }
-
- queue_network_changed (monitor);
- return;
+ case G_SOCKET_FAMILY_IPV4:
+ monitor->priv->have_ipv4_default_route = FALSE;
+ break;
+ case G_SOCKET_FAMILY_IPV6:
+ monitor->priv->have_ipv6_default_route = FALSE;
+ break;
+ default:
+ break;
}
}
+
+ queue_network_changed (monitor);
}
/**
@@ -526,7 +576,7 @@ g_network_monitor_base_set_networks (GNetworkMonitorBase *monitor,
{
int i;
- g_ptr_array_set_size (monitor->priv->networks, 0);
+ g_hash_table_remove_all (monitor->priv->networks);
monitor->priv->have_ipv4_default_route = FALSE;
monitor->priv->have_ipv6_default_route = FALSE;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]