Commit 394ed959 authored by Thomas Haller's avatar Thomas Haller
Browse files

core: merge branch 'th/dedup-multi-v2-bgo785004'

https://bugzilla.gnome.org/show_bug.cgi?id=785004
parents c528a895 5f995123
......@@ -414,6 +414,7 @@ libnm_core_lib_h_pub_real = \
libnm_core_lib_h_pub_mkenums = \
libnm-core/nm-core-enum-types.h
libnm_core_lib_h_priv = \
shared/nm-utils/c-list-util.h \
shared/nm-utils/nm-dedup-multi.h \
shared/nm-utils/nm-enum-utils.h \
shared/nm-utils/nm-shared-utils.h \
......@@ -429,6 +430,7 @@ libnm_core_lib_h_priv = \
libnm-core/nm-setting-private.h \
libnm-core/nm-utils-private.h
libnm_core_lib_c_real = \
shared/nm-utils/c-list-util.c \
shared/nm-utils/nm-dedup-multi.c \
shared/nm-utils/nm-enum-utils.c \
shared/nm-utils/nm-shared-utils.c \
......@@ -4437,6 +4439,8 @@ EXTRA_DIST += \
shared/nm-test-libnm-utils.h \
shared/nm-test-utils-impl.c \
shared/nm-utils/c-list.h \
shared/nm-utils/c-list-util.c \
shared/nm-utils/c-list-util.h \
shared/nm-utils/gsystem-local-alloc.h \
shared/nm-utils/nm-glib.h \
shared/nm-utils/nm-obj.h \
......
......@@ -25,6 +25,8 @@
#include <string.h>
#include "nm-utils/c-list-util.h"
#include "nm-utils.h"
#include "nm-setting-private.h"
#include "nm-utils.h"
......@@ -76,6 +78,103 @@ G_STATIC_ASSERT (sizeof (bool) <= sizeof (int));
/*****************************************************************************/
typedef struct {
int val;
int idx;
CList lst;
} CListSort;
static int
_c_list_sort_cmp (const CList *lst_a, const CList *lst_b, const void *user_data)
{
const CListSort *a, *b;
g_assert (lst_a);
g_assert (lst_b);
g_assert (lst_a != lst_b);
a = c_list_entry (lst_a, CListSort, lst);
b = c_list_entry (lst_b, CListSort, lst);
if (a->val < b->val)
return -1;
if (a->val > b->val)
return 1;
return 0;
}
static void
test_c_list_sort (void)
{
guint i, n_list, repeat, headless;
CList head, *iter, *iter_prev, *lst;
CListSort elements[30];
const CListSort *el_prev;
c_list_init (&head);
c_list_sort (&head, _c_list_sort_cmp, NULL);
g_assert (c_list_length (&head) == 0);
g_assert (c_list_is_empty (&head));
for (repeat = 0; repeat < 10; repeat++) {
for (n_list = 1; n_list < G_N_ELEMENTS (elements); n_list++) {
for (headless = 0; headless < 2; headless++) {
c_list_init (&head);
for (i = 0; i < n_list; i++) {
CListSort *el;
el = &elements[i];
el->val = nmtst_get_rand_int () % (2*n_list);
el->idx = i;
c_list_link_tail (&head, &el->lst);
}
if (headless) {
lst = head.next;
c_list_unlink (&head);
lst = c_list_sort_headless (lst, _c_list_sort_cmp, NULL);
g_assert (lst);
g_assert (lst->next);
g_assert (lst->prev);
g_assert (c_list_length (lst) == n_list - 1);
iter_prev = lst->prev;
for (iter = lst; iter != lst; iter = iter->next) {
g_assert (iter);
g_assert (iter->next);
g_assert (iter->prev == iter_prev);
}
c_list_link_before (lst, &head);
} else {
c_list_sort (&head, _c_list_sort_cmp, NULL);
}
g_assert (!c_list_is_empty (&head));
g_assert (c_list_length (&head) == n_list);
el_prev = NULL;
c_list_for_each (iter, &head) {
CListSort *el;
el = c_list_entry (iter, CListSort, lst);
g_assert (el->idx >= 0 && el->idx < n_list);
g_assert (el == &elements[el->idx]);
if (el_prev) {
g_assert (el_prev->val <= el->val);
if (el_prev->val == el->val)
g_assert (el_prev->idx < el->idx);
g_assert (iter->prev == &el_prev->lst);
g_assert (el_prev->lst.next == iter);
}
el_prev = el;
}
g_assert (head.prev == &el_prev->lst);
}
}
}
}
/*****************************************************************************/
typedef struct {
NMDedupMultiObj parent;
guint val;
......@@ -6078,6 +6177,7 @@ int main (int argc, char **argv)
{
nmtst_init (&argc, &argv, TRUE);
g_test_add_func ("/core/general/test_c_list_sort", test_c_list_sort);
g_test_add_func ("/core/general/test_dedup_multi", test_dedup_multi);
g_test_add_func ("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe);
g_test_add_func ("/core/general/test_nm_in_set", test_nm_in_set);
......
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* (C) Copyright 2017 Red Hat, Inc.
*/
#include "c-list-util.h"
/*****************************************************************************/
/**
* c_list_relink:
* @lst: the head list entry
*
* Takes an invalid list, that has undefined prev pointers.
* Only the next pointers are valid, and the tail's next
* pointer points to %NULL instead of the head.
*
* c_list_relink() fixes the list by updating all prev pointers
* and close the circular linking by pointing the tails' next
* pointer to @lst.
*
* The use of this function is to do a bulk update, that lets the
* list degredate by not updating the prev pointers. At the end,
* the list can be fixed by c_list_relink().
*/
void
c_list_relink (CList *lst)
{
CList *ls, *ls_prev;
ls_prev = lst;
ls = lst->next;
do {
ls->prev = ls_prev;
ls_prev = ls;
ls = ls->next;
} while (ls);
ls_prev->next = lst;
lst->prev = ls_prev;
}
/*****************************************************************************/
static CList *
_c_list_sort (CList *ls,
CListSortCmp cmp,
const void *user_data)
{
CList *ls1, *ls2;
CList head;
if (!ls->next)
return ls;
/* split list in two halfs @ls1 and @ls2. */
ls1 = ls;
ls2 = ls;
ls = ls->next;
while (ls) {
ls = ls->next;
if (!ls)
break;
ls = ls->next;
ls2 = ls2->next;
}
ls = ls2;
ls2 = ls->next;
ls->next = NULL;
/* recurse */
ls1 = _c_list_sort (ls1, cmp, user_data);
if (!ls2)
return ls1;
ls2 = _c_list_sort (ls2, cmp, user_data);
/* merge */
ls = &head;
for (;;) {
/* while invoking the @cmp function, the list
* elements are not properly linked. Don't try to access
* their next/prev pointers. */
if (cmp (ls1, ls2, user_data) <= 0) {
ls->next = ls1;
ls = ls1;
ls1 = ls1->next;
if (!ls1)
break;
} else {
ls->next = ls2;
ls = ls2;
ls2 = ls2->next;
if (!ls2)
break;
}
}
ls->next = ls1 ?: ls2;
return head.next;
}
/**
* c_list_sort_headless:
* @lst: the list.
* @cmp: compare function for sorting. While comparing two
* CList elements, their next/prev pointers are in undefined
* state.
* @user_data: user data for @cmp.
*
* Sorts the list @lst according to @cmp. Contrary to
* c_list_sort(), @lst is not the list head but a
* valid entry as well. This function returns the new
* list head.
*/
CList *
c_list_sort_headless (CList *lst,
CListSortCmp cmp,
const void *user_data)
{
if (!c_list_is_empty (lst)) {
lst->prev->next = NULL;
lst = _c_list_sort (lst, cmp, user_data);
c_list_relink (lst);
}
return lst;
}
/**
* c_list_sort:
* @head: the list head.
* @cmp: compare function for sorting. While comparing two
* CList elements, their next/prev pointers are in undefined
* state.
* @user_data: user data for @cmp.
*
* Sorts the list @head according to @cmp.
*/
void
c_list_sort (CList *head,
CListSortCmp cmp,
const void *user_data)
{
if ( !c_list_is_empty (head)
&& head->next->next != head) {
head->prev->next = NULL;
head->next = _c_list_sort (head->next, cmp, user_data);
c_list_relink (head);
}
}
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* (C) Copyright 2017 Red Hat, Inc.
*/
#ifndef __C_LIST_UTIL_H__
#define __C_LIST_UTIL_H__
#include "c-list.h"
/*****************************************************************************/
void c_list_relink (CList *lst);
typedef int (*CListSortCmp) (const CList *a,
const CList *b,
const void *user_data);
CList *c_list_sort_headless (CList *lst,
CListSortCmp cmp,
const void *user_data);
void c_list_sort (CList *head,
CListSortCmp cmp,
const void *user_data);
#endif /* __C_LIST_UTIL_H__ */
......@@ -927,21 +927,60 @@ nm_dedup_multi_obj_clone (const NMDedupMultiObj *obj)
return o;
}
gconstpointer *
nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry,
NMDedupMultiFcnSelectPredicate predicate,
gpointer user_data,
guint *out_len)
{
gconstpointer *result;
CList *iter;
guint i;
if (!head_entry) {
NM_SET_OUT (out_len, 0);
return NULL;
}
result = g_new (gconstpointer, head_entry->len + 1);
i = 0;
c_list_for_each (iter, &head_entry->lst_entries_head) {
const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj;
if ( !predicate
|| predicate (obj, user_data)) {
nm_assert (i < head_entry->len);
result[i++] = obj;
}
}
if (i == 0) {
g_free (result);
NM_SET_OUT (out_len, 0);
return NULL;
}
nm_assert (i <= head_entry->len);
NM_SET_OUT (out_len, i);
result[i++] = NULL;
return result;
}
GPtrArray *
nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry,
NMDedupMultiFcnSelectPredicate predicate,
gpointer user_data)
{
GPtrArray *result;
NMDedupMultiIter iter;
CList *iter;
if (!head_entry)
return NULL;
result = g_ptr_array_new_full (head_entry->len,
(GDestroyNotify) nm_dedup_multi_obj_unref);
nm_dedup_multi_iter_for_each (&iter, head_entry) {
const NMDedupMultiObj *obj = iter.current->obj;
c_list_for_each (iter, &head_entry->lst_entries_head) {
const NMDedupMultiObj *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj;
if ( !predicate
|| predicate (obj, user_data))
......
......@@ -23,7 +23,7 @@
#define __NM_DEDUP_MULTI_H__
#include "nm-obj.h"
#include "c-list.h"
#include "c-list-util.h"
/*****************************************************************************/
......@@ -302,9 +302,9 @@ guint nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self,
/*****************************************************************************/
typedef struct _NMDedupMultiIter {
const NMDedupMultiHeadEntry *head;
const CList *_head;
const CList *_next;
const NMDedupMultiEntry *current;
const NMDedupMultiEntry *next;
} NMDedupMultiIter;
static inline void
......@@ -312,11 +312,14 @@ nm_dedup_multi_iter_init (NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *h
{
g_return_if_fail (iter);
iter->head = head;
if (head && !c_list_is_empty (&head->lst_entries_head)) {
iter->_head = &head->lst_entries_head;
iter->_next = head->lst_entries_head.next;
} else {
iter->_head = NULL;
iter->_next = NULL;
}
iter->current = NULL;
iter->next = head && !c_list_is_empty (&head->lst_entries_head)
? c_list_entry (head->lst_entries_head.next, NMDedupMultiEntry, lst_entries)
: NULL;
}
static inline gboolean
......@@ -324,42 +327,19 @@ nm_dedup_multi_iter_next (NMDedupMultiIter *iter)
{
g_return_val_if_fail (iter, FALSE);
if (!iter->next)
if (!iter->_next)
return FALSE;
/* we always look ahead for the @next. This way, the user
/* we always look ahead for the next. This way, the user
* may delete the current entry (but no other entries). */
iter->current = iter->next;
if (iter->next->lst_entries.next == &iter->head->lst_entries_head)
iter->next = NULL;
iter->current = c_list_entry (iter->_next, NMDedupMultiEntry, lst_entries);
if (iter->_next->next == iter->_head)
iter->_next = NULL;
else
iter->next = c_list_entry (iter->next->lst_entries.next, NMDedupMultiEntry, lst_entries);
iter->_next = iter->_next->next;
return TRUE;
}
static inline void
nm_dedup_multi_iter_rewind (NMDedupMultiIter *iter)
{
/* rewind the iterator.
*
* In principle, you can always delete the current entry.
* However, if you delete *all* current entries, the list
* head becomes invalid too and rewinding will crash.
*
* So, either
* - don't modify the list
* - if you modify it:
* - only delete the current entry, don't delete other entries.
* - you may add more entries, however that may make iteration
* confusing.
* - you may rewind the iterator, but only if not all
* entires were deleted.
*
* Use with care. */
g_return_if_fail (iter);
nm_dedup_multi_iter_init (iter, iter->head);
}
#define nm_dedup_multi_iter_for_each(iter, head_entry) \
for (nm_dedup_multi_iter_init ((iter), (head_entry)); \
nm_dedup_multi_iter_next ((iter)); \
......@@ -370,10 +350,27 @@ nm_dedup_multi_iter_rewind (NMDedupMultiIter *iter)
typedef gboolean (*NMDedupMultiFcnSelectPredicate) (/* const NMDedupMultiObj * */ gconstpointer obj,
gpointer user_data);
gconstpointer *nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry,
NMDedupMultiFcnSelectPredicate predicate,
gpointer user_data,
guint *out_len);
GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry,
NMDedupMultiFcnSelectPredicate predicate,
gpointer user_data);
static inline void
nm_dedup_multi_head_entry_sort (const NMDedupMultiHeadEntry *head_entry,
CListSortCmp cmp,
gconstpointer user_data)
{
if (head_entry) {
/* the head entry can be sorted directly without messing up the
* index to which it belongs. Of course, this does mess up any
* NMDedupMultiIter instances. */
c_list_sort ((CList *) &head_entry->lst_entries_head, cmp, user_data);
}
}
/*****************************************************************************/
#endif /* __NM_DEDUP_MULTI_H__ */
......@@ -2454,16 +2454,19 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self)
gint32 now;
GArray *addresses, *dns_servers, *dns_domains;
guint len, i;
const NMDedupMultiHeadEntry *head_entry;
NMDedupMultiIter ipconf_iter;
if (nm_ndisc_get_node_type (ndisc) != NM_NDISC_NODE_TYPE_ROUTER)
return;
now = nm_utils_get_monotonic_timestamp_s ();
len = nm_ip6_config_get_num_addresses (priv->ip6_config);
addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress), len);
for (i = 0; i < len; i++) {
const NMPlatformIP6Address *addr = nm_ip6_config_get_address (priv->ip6_config, i);
head_entry = nm_ip6_config_lookup_addresses (priv->ip6_config);
addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress),
head_entry ? head_entry->len : 0);
nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) {
const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj);
NMNDiscAddress *ndisc_addr;
if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
......@@ -5095,17 +5098,17 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da
{
NMDevice *self;
NMDevicePrivate *priv;
NMDedupMultiIter ipconf_iter;
const NMPlatformIP4Address *address;
gboolean result, success = TRUE;
int i, j;
int i;
g_assert (data);
self = data->device;
priv = NM_DEVICE_GET_PRIVATE (self);
for (i = 0; data->configs && data->configs[i]; i++) {
for (j = 0; j < nm_ip4_config_get_num_addresses (data->configs[i]); j++) {
address = nm_ip4_config_get_address (data->configs[i], j);
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) {
result = nm_arping_manager_check_address (arping_manager, address->address);
success &= result;
......@@ -5139,13 +5142,14 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMArpingManager *arping_manager;
const NMPlatformIP4Address *address;
NMDedupMultiIter ipconf_iter;
ArpingData *data;
guint timeout;
gboolean ret, addr_found;
const guint8 *hw_addr;
size_t hw_addr_len = 0;
GError *error = NULL;
guint i, j;
guint i;
g_return_if_fail (NM_IS_DEVICE (self));
g_return_if_fail (configs);
......@@ -5191,10 +5195,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
data->device = self;
for (i = 0; configs[i]; i++) {
for (j = 0; j < nm_ip4_config_get_num_addresses (configs[i]); j++) {
address = nm_ip4_config_get_address (configs[i], j);
nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address)
nm_arping_manager_add_address (arping_manager, address->address);
}
}
g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED,
......@@ -6618,7 +6620,6 @@ dhcp6_state_changed (NMDhcpClient *client,
{
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
guint i;
g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config));
......@@ -6635,10 +6636,11 @@ dhcp6_state_changed (NMDhcpClient *client,