mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
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
This commit is contained in:
parent
0d9d548932
commit
4a488ced5d
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user