Blame shared/nm-glib-aux/nm-dedup-multi.h

Packit Service b23acc
// SPDX-License-Identifier: LGPL-2.1+
Packit Service b23acc
/*
Packit Service b23acc
 * Copyright (C) 2017 Red Hat, Inc.
Packit Service b23acc
 */
Packit Service b23acc
Packit Service b23acc
#ifndef __NM_DEDUP_MULTI_H__
Packit Service b23acc
#define __NM_DEDUP_MULTI_H__
Packit Service b23acc
Packit Service b23acc
#include "nm-obj.h"
Packit Service b23acc
#include "nm-std-aux/c-list-util.h"
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
struct _NMHashState;
Packit Service b23acc
Packit Service b23acc
typedef struct _NMDedupMultiObj             NMDedupMultiObj;
Packit Service b23acc
typedef struct _NMDedupMultiObjClass        NMDedupMultiObjClass;
Packit Service b23acc
typedef struct _NMDedupMultiIdxType         NMDedupMultiIdxType;
Packit Service b23acc
typedef struct _NMDedupMultiIdxTypeClass    NMDedupMultiIdxTypeClass;
Packit Service b23acc
typedef struct _NMDedupMultiEntry           NMDedupMultiEntry;
Packit Service b23acc
typedef struct _NMDedupMultiHeadEntry       NMDedupMultiHeadEntry;
Packit Service b23acc
typedef struct _NMDedupMultiIndex           NMDedupMultiIndex;
Packit Service b23acc
Packit Service b23acc
typedef enum _NMDedupMultiIdxMode {
Packit Service b23acc
	NM_DEDUP_MULTI_IDX_MODE_PREPEND,
Packit Service b23acc
Packit Service b23acc
	NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE,
Packit Service b23acc
Packit Service b23acc
	/* append new objects to the end of the list.
Packit Service b23acc
	 * If the object is already in the cache, don't move it. */
Packit Service b23acc
	NM_DEDUP_MULTI_IDX_MODE_APPEND,
Packit Service b23acc
Packit Service b23acc
	/* like NM_DEDUP_MULTI_IDX_MODE_APPEND, but if the object
Packit Service b23acc
	 * is already in the cache, move it to the end. */
Packit Service b23acc
	NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE,
Packit Service b23acc
} NMDedupMultiIdxMode;
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
struct _NMDedupMultiObj {
Packit Service b23acc
	union {
Packit Service b23acc
		NMObjBaseInst parent;
Packit Service b23acc
		const NMDedupMultiObjClass *klass;
Packit Service b23acc
	};
Packit Service b23acc
	NMDedupMultiIndex *_multi_idx;
Packit Service b23acc
	guint _ref_count;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
struct _NMDedupMultiObjClass {
Packit Service b23acc
	NMObjBaseClass parent;
Packit Service b23acc
Packit Service b23acc
	const NMDedupMultiObj *(*obj_clone) (const NMDedupMultiObj *obj);
Packit Service b23acc
Packit Service b23acc
	gboolean (*obj_needs_clone) (const NMDedupMultiObj *obj);
Packit Service b23acc
Packit Service b23acc
	void (*obj_destroy) (NMDedupMultiObj *obj);
Packit Service b23acc
Packit Service b23acc
	/* the NMDedupMultiObj can be deduplicated. For that the obj_full_hash_update()
Packit Service b23acc
	 * and obj_full_equal() compare *all* fields of the object, even minor ones. */
Packit Service b23acc
	void (*obj_full_hash_update)  (const NMDedupMultiObj *obj,
Packit Service b23acc
	                               struct _NMHashState *h);
Packit Service b23acc
	gboolean (*obj_full_equal) (const NMDedupMultiObj *obj_a,
Packit Service b23acc
	                            const NMDedupMultiObj *obj_b);
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static inline const NMDedupMultiObj *
Packit Service b23acc
nm_dedup_multi_obj_ref (const NMDedupMultiObj *obj)
Packit Service b23acc
{
Packit Service b23acc
	/* ref and unref accept const pointers. Objects is supposed to be shared
Packit Service b23acc
	 * and kept immutable. Disallowing to take/return a reference to a const
Packit Service b23acc
	 * NMPObject is cumbersome, because callers are precisely expected to
Packit Service b23acc
	 * keep a ref on the otherwise immutable object. */
Packit Service b23acc
Packit Service b23acc
	nm_assert (obj);
Packit Service b23acc
	nm_assert (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT);
Packit Service b23acc
	nm_assert (obj->_ref_count > 0);
Packit Service b23acc
Packit Service b23acc
	((NMDedupMultiObj *) obj)->_ref_count++;
Packit Service b23acc
	return obj;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
void                   nm_dedup_multi_obj_unref       (const NMDedupMultiObj *obj);
Packit Service b23acc
const NMDedupMultiObj *nm_dedup_multi_obj_clone       (const NMDedupMultiObj *obj);
Packit Service b23acc
gboolean               nm_dedup_multi_obj_needs_clone (const NMDedupMultiObj *obj);
Packit Service b23acc
Packit Service b23acc
gconstpointer nm_dedup_multi_index_obj_intern (NMDedupMultiIndex *self,
Packit Service b23acc
                                               /* const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
void nm_dedup_multi_index_obj_release (NMDedupMultiIndex *self,
Packit Service b23acc
                                       /* const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
/* const NMDedupMultiObj * */ gconstpointer nm_dedup_multi_index_obj_find (NMDedupMultiIndex *self,
Packit Service b23acc
                                                                           /* const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
/* the NMDedupMultiIdxType is an access handle under which you can store and
Packit Service b23acc
 * retrieve NMDedupMultiObj instances in NMDedupMultiIndex.
Packit Service b23acc
 *
Packit Service b23acc
 * The NMDedupMultiIdxTypeClass determines its behavior, but you can have
Packit Service b23acc
 * multiple instances (of the same class).
Packit Service b23acc
 *
Packit Service b23acc
 * For example, NMIP4Config can have idx-type to put there all IPv4 Routes.
Packit Service b23acc
 * This idx-type instance is private to the NMIP4Config instance. Basically,
Packit Service b23acc
 * the NMIP4Config instance uses the idx-type to maintain an ordered list
Packit Service b23acc
 * of routes in NMDedupMultiIndex.
Packit Service b23acc
 *
Packit Service b23acc
 * However, a NMDedupMultiIdxType may also partition the set of objects
Packit Service b23acc
 * in multiple distinct lists. NMIP4Config doesn't do that (because instead
Packit Service b23acc
 * of creating one idx-type for IPv4 and IPv6 routes, it just cretaes
Packit Service b23acc
 * to distinct idx-types, one for each address family.
Packit Service b23acc
 * This partitioning is used by NMPlatform to maintain a lookup index for
Packit Service b23acc
 * routes by ifindex. As the ifindex is dynamic, it does not create an
Packit Service b23acc
 * idx-type instance for each ifindex. Instead, it has one idx-type for
Packit Service b23acc
 * all routes. But whenever accessing NMDedupMultiIndex with an NMDedupMultiObj,
Packit Service b23acc
 * the partitioning NMDedupMultiIdxType takes into account the NMDedupMultiObj
Packit Service b23acc
 * instance to associate it with the right list.
Packit Service b23acc
 *
Packit Service b23acc
 * Hence, a NMDedupMultiIdxEntry has a list of possibly multiple NMDedupMultiHeadEntry
Packit Service b23acc
 * instances, which each is the head for a list of NMDedupMultiEntry instances.
Packit Service b23acc
 * In the platform example, the NMDedupMultiHeadEntry partition the indexed objects
Packit Service b23acc
 * by their ifindex. */
Packit Service b23acc
struct _NMDedupMultiIdxType {
Packit Service b23acc
	union {
Packit Service b23acc
		NMObjBaseInst parent;
Packit Service b23acc
		const NMDedupMultiIdxTypeClass *klass;
Packit Service b23acc
	};
Packit Service b23acc
Packit Service b23acc
	CList lst_idx_head;
Packit Service b23acc
Packit Service b23acc
	guint len;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
void nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                   const NMDedupMultiIdxTypeClass *klass);
Packit Service b23acc
Packit Service b23acc
struct _NMDedupMultiIdxTypeClass {
Packit Service b23acc
	NMObjBaseClass parent;
Packit Service b23acc
Packit Service b23acc
	void (*idx_obj_id_hash_update)  (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
	                                 const NMDedupMultiObj *obj,
Packit Service b23acc
	                                 struct _NMHashState *h);
Packit Service b23acc
	gboolean (*idx_obj_id_equal) (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
	                              const NMDedupMultiObj *obj_a,
Packit Service b23acc
	                              const NMDedupMultiObj *obj_b);
Packit Service b23acc
Packit Service b23acc
	/* an NMDedupMultiIdxTypeClass which implements partitioning of the
Packit Service b23acc
	 * tracked objects, must implement the idx_obj_partition*() functions.
Packit Service b23acc
	 *
Packit Service b23acc
	 * idx_obj_partitionable() may return NULL if the object cannot be tracked.
Packit Service b23acc
	 * For example, a index for routes by ifindex, may not want to track any
Packit Service b23acc
	 * routes that don't have a valid ifindex. If the idx-type says that the
Packit Service b23acc
	 * object is not partitionable, it is never added to the NMDedupMultiIndex. */
Packit Service b23acc
	gboolean (*idx_obj_partitionable)   (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
	                                     const NMDedupMultiObj *obj);
Packit Service b23acc
	void (*idx_obj_partition_hash_update) (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
	                                       const NMDedupMultiObj *obj,
Packit Service b23acc
	                                       struct _NMHashState *h);
Packit Service b23acc
	gboolean (*idx_obj_partition_equal) (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
	                                     const NMDedupMultiObj *obj_a,
Packit Service b23acc
	                                     const NMDedupMultiObj *obj_b);
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
static inline gboolean
Packit Service b23acc
nm_dedup_multi_idx_type_id_equal (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                  /* const NMDedupMultiObj * */ gconstpointer obj_a,
Packit Service b23acc
                                  /* const NMDedupMultiObj * */ gconstpointer obj_b)
Packit Service b23acc
{
Packit Service b23acc
	nm_assert (idx_type);
Packit Service b23acc
	return    obj_a == obj_b
Packit Service b23acc
	       || idx_type->klass->idx_obj_id_equal (idx_type,
Packit Service b23acc
	                                             obj_a,
Packit Service b23acc
	                                             obj_b);
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static inline gboolean
Packit Service b23acc
nm_dedup_multi_idx_type_partition_equal (const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                         /* const NMDedupMultiObj * */ gconstpointer obj_a,
Packit Service b23acc
                                         /* const NMDedupMultiObj * */ gconstpointer obj_b)
Packit Service b23acc
{
Packit Service b23acc
	nm_assert (idx_type);
Packit Service b23acc
	if (idx_type->klass->idx_obj_partition_equal) {
Packit Service b23acc
		nm_assert (obj_a);
Packit Service b23acc
		nm_assert (obj_b);
Packit Service b23acc
		return    obj_a == obj_b
Packit Service b23acc
		       || idx_type->klass->idx_obj_partition_equal (idx_type,
Packit Service b23acc
		                                                    obj_a,
Packit Service b23acc
		                                                    obj_b);
Packit Service b23acc
	}
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
struct _NMDedupMultiEntry {
Packit Service b23acc
Packit Service b23acc
	/* this is the list of all entries that share the same head entry.
Packit Service b23acc
	 * All entries compare equal according to idx_obj_partition_equal(). */
Packit Service b23acc
	CList lst_entries;
Packit Service b23acc
Packit Service b23acc
	/* const NMDedupMultiObj * */ gconstpointer obj;
Packit Service b23acc
Packit Service b23acc
	bool is_head;
Packit Service b23acc
	bool dirty;
Packit Service b23acc
Packit Service b23acc
	const NMDedupMultiHeadEntry *head;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
struct _NMDedupMultiHeadEntry {
Packit Service b23acc
Packit Service b23acc
	/* this is the list of all entries that share the same head entry.
Packit Service b23acc
	 * All entries compare equal according to idx_obj_partition_equal(). */
Packit Service b23acc
	CList lst_entries_head;
Packit Service b23acc
Packit Service b23acc
	const NMDedupMultiIdxType *idx_type;
Packit Service b23acc
Packit Service b23acc
	bool is_head;
Packit Service b23acc
Packit Service b23acc
	guint len;
Packit Service b23acc
Packit Service b23acc
	CList lst_idx;
Packit Service b23acc
};
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static inline gconstpointer
Packit Service b23acc
nm_dedup_multi_entry_get_obj (const NMDedupMultiEntry *entry)
Packit Service b23acc
{
Packit Service b23acc
	/* convenience method that allows to skip the %NULL check on
Packit Service b23acc
	 * @entry. Think of the NULL-conditional operator ?. of C# */
Packit Service b23acc
	return entry ? entry->obj : NULL;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
static inline void
Packit Service b23acc
nm_dedup_multi_entry_set_dirty (const NMDedupMultiEntry *entry,
Packit Service b23acc
                                gboolean dirty)
Packit Service b23acc
{
Packit Service b23acc
	/* NMDedupMultiEntry is always exposed as a const object, because it is not
Packit Service b23acc
	 * supposed to be modified outside NMDedupMultiIndex API. Except the "dirty"
Packit Service b23acc
	 * flag. In C++ speak, it is a mutable field.
Packit Service b23acc
	 *
Packit Service b23acc
	 * Add this inline function, to cast-away constness and set the dirty flag. */
Packit Service b23acc
	nm_assert (entry);
Packit Service b23acc
	((NMDedupMultiEntry *) entry)->dirty = dirty;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
NMDedupMultiIndex *nm_dedup_multi_index_new (void);
Packit Service b23acc
NMDedupMultiIndex *nm_dedup_multi_index_ref (NMDedupMultiIndex *self);
Packit Service b23acc
NMDedupMultiIndex *nm_dedup_multi_index_unref (NMDedupMultiIndex *self);
Packit Service b23acc
Packit Service b23acc
static inline void
Packit Service b23acc
_nm_auto_unref_dedup_multi_index (NMDedupMultiIndex **v)
Packit Service b23acc
{
Packit Service b23acc
	if (*v)
Packit Service b23acc
		nm_dedup_multi_index_unref (*v);
Packit Service b23acc
}
Packit Service b23acc
#define nm_auto_unref_dedup_multi_index nm_auto(_nm_auto_unref_dedup_multi_index)
Packit Service b23acc
Packit Service b23acc
#define NM_DEDUP_MULTI_ENTRY_MISSING      ((const NMDedupMultiEntry *)     GUINT_TO_POINTER (1))
Packit Service b23acc
#define NM_DEDUP_MULTI_HEAD_ENTRY_MISSING ((const NMDedupMultiHeadEntry *) GUINT_TO_POINTER (1))
Packit Service b23acc
Packit Service b23acc
gboolean nm_dedup_multi_index_add_full (NMDedupMultiIndex *self,
Packit Service b23acc
                                        NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                        /*const NMDedupMultiObj * */ gconstpointer obj,
Packit Service b23acc
                                        NMDedupMultiIdxMode mode,
Packit Service b23acc
                                        const NMDedupMultiEntry *entry_order,
Packit Service b23acc
                                        const NMDedupMultiEntry *entry_existing,
Packit Service b23acc
                                        const NMDedupMultiHeadEntry *head_existing,
Packit Service b23acc
                                        const NMDedupMultiEntry **out_entry,
Packit Service b23acc
                                        /* const NMDedupMultiObj ** */ gpointer out_obj_old);
Packit Service b23acc
Packit Service b23acc
gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self,
Packit Service b23acc
                                   NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                   /*const NMDedupMultiObj * */ gconstpointer obj,
Packit Service b23acc
                                   NMDedupMultiIdxMode mode,
Packit Service b23acc
                                   const NMDedupMultiEntry **out_entry,
Packit Service b23acc
                                   /* const NMDedupMultiObj ** */ gpointer out_obj_old);
Packit Service b23acc
Packit Service b23acc
const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self,
Packit Service b23acc
                                                          const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                                          /*const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self,
Packit Service b23acc
                                                               const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                                               /*const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
guint nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self,
Packit Service b23acc
                                         gconstpointer entry);
Packit Service b23acc
Packit Service b23acc
guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self,
Packit Service b23acc
                                       NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                       /*const NMDedupMultiObj * */ gconstpointer obj,
Packit Service b23acc
                                       /*const NMDedupMultiObj ** */ gconstpointer *out_obj);
Packit Service b23acc
Packit Service b23acc
guint nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self,
Packit Service b23acc
                                        NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                        /*const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
guint nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self,
Packit Service b23acc
                                       NMDedupMultiIdxType *idx_type);
Packit Service b23acc
Packit Service b23acc
void nm_dedup_multi_index_dirty_set_head (NMDedupMultiIndex *self,
Packit Service b23acc
                                          const NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                          /*const NMDedupMultiObj * */ gconstpointer obj);
Packit Service b23acc
Packit Service b23acc
void nm_dedup_multi_index_dirty_set_idx (NMDedupMultiIndex *self,
Packit Service b23acc
                                         const NMDedupMultiIdxType *idx_type);
Packit Service b23acc
Packit Service b23acc
guint nm_dedup_multi_index_dirty_remove_idx (NMDedupMultiIndex *self,
Packit Service b23acc
                                             NMDedupMultiIdxType *idx_type,
Packit Service b23acc
                                             gboolean mark_survivors_dirty);
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
typedef struct _NMDedupMultiIter {
Packit Service b23acc
	const CList *_head;
Packit Service b23acc
	const CList *_next;
Packit Service b23acc
	const NMDedupMultiEntry *current;
Packit Service b23acc
} NMDedupMultiIter;
Packit Service b23acc
Packit Service b23acc
static inline void
Packit Service b23acc
nm_dedup_multi_iter_init (NMDedupMultiIter *iter, const NMDedupMultiHeadEntry *head)
Packit Service b23acc
{
Packit Service b23acc
	g_return_if_fail (iter);
Packit Service b23acc
Packit Service b23acc
	if (head && !c_list_is_empty (&head->lst_entries_head)) {
Packit Service b23acc
		iter->_head = &head->lst_entries_head;
Packit Service b23acc
		iter->_next = head->lst_entries_head.next;
Packit Service b23acc
	} else {
Packit Service b23acc
		iter->_head = NULL;
Packit Service b23acc
		iter->_next = NULL;
Packit Service b23acc
	}
Packit Service b23acc
	iter->current = NULL;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static inline gboolean
Packit Service b23acc
nm_dedup_multi_iter_next (NMDedupMultiIter *iter)
Packit Service b23acc
{
Packit Service b23acc
	g_return_val_if_fail (iter, FALSE);
Packit Service b23acc
Packit Service b23acc
	if (!iter->_next)
Packit Service b23acc
		return FALSE;
Packit Service b23acc
Packit Service b23acc
	/* we always look ahead for the next. This way, the user
Packit Service b23acc
	 * may delete the current entry (but no other entries). */
Packit Service b23acc
	iter->current = c_list_entry (iter->_next, NMDedupMultiEntry, lst_entries);
Packit Service b23acc
	if (iter->_next->next == iter->_head)
Packit Service b23acc
		iter->_next = NULL;
Packit Service b23acc
	else
Packit Service b23acc
		iter->_next = iter->_next->next;
Packit Service b23acc
	return TRUE;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
#define nm_dedup_multi_iter_for_each(iter, head_entry) \
Packit Service b23acc
	for (nm_dedup_multi_iter_init ((iter), (head_entry)); \
Packit Service b23acc
	     nm_dedup_multi_iter_next ((iter)); \
Packit Service b23acc
	     )
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
typedef gboolean (*NMDedupMultiFcnSelectPredicate) (/* const NMDedupMultiObj * */ gconstpointer obj,
Packit Service b23acc
                                                    gpointer user_data);
Packit Service b23acc
Packit Service b23acc
gconstpointer *nm_dedup_multi_objs_to_array_head (const NMDedupMultiHeadEntry *head_entry,
Packit Service b23acc
                                                  NMDedupMultiFcnSelectPredicate predicate,
Packit Service b23acc
                                                  gpointer user_data,
Packit Service b23acc
                                                  guint *out_len);
Packit Service b23acc
GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *head_entry,
Packit Service b23acc
                                                  NMDedupMultiFcnSelectPredicate predicate,
Packit Service b23acc
                                                  gpointer user_data);
Packit Service b23acc
Packit Service b23acc
static inline const NMDedupMultiEntry *
Packit Service b23acc
nm_dedup_multi_head_entry_get_idx (const NMDedupMultiHeadEntry *head_entry,
Packit Service b23acc
                                   int idx)
Packit Service b23acc
{
Packit Service b23acc
	CList *iter;
Packit Service b23acc
Packit Service b23acc
	if (head_entry) {
Packit Service b23acc
		if (idx >= 0) {
Packit Service b23acc
			c_list_for_each (iter, &head_entry->lst_entries_head) {
Packit Service b23acc
				if (idx-- == 0)
Packit Service b23acc
					return c_list_entry (iter, NMDedupMultiEntry, lst_entries);
Packit Service b23acc
			}
Packit Service b23acc
		} else {
Packit Service b23acc
			for (iter = head_entry->lst_entries_head.prev;
Packit Service b23acc
			     iter != &head_entry->lst_entries_head;
Packit Service b23acc
			     iter = iter->prev) {
Packit Service b23acc
				if (++idx == 0)
Packit Service b23acc
					return c_list_entry (iter, NMDedupMultiEntry, lst_entries);
Packit Service b23acc
			}
Packit Service b23acc
		}
Packit Service b23acc
	}
Packit Service b23acc
	return NULL;
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
static inline void
Packit Service b23acc
nm_dedup_multi_head_entry_sort (const NMDedupMultiHeadEntry *head_entry,
Packit Service b23acc
                                CListSortCmp cmp,
Packit Service b23acc
                                gconstpointer user_data)
Packit Service b23acc
{
Packit Service b23acc
	if (head_entry) {
Packit Service b23acc
		/* the head entry can be sorted directly without messing up the
Packit Service b23acc
		 * index to which it belongs. Of course, this does mess up any
Packit Service b23acc
		 * NMDedupMultiIter instances. */
Packit Service b23acc
		c_list_sort ((CList *) &head_entry->lst_entries_head, cmp, user_data);
Packit Service b23acc
	}
Packit Service b23acc
}
Packit Service b23acc
Packit Service b23acc
gboolean nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry,
Packit Service b23acc
                                       const NMDedupMultiEntry *entry_order,
Packit Service b23acc
                                       gboolean order_after);
Packit Service b23acc
Packit Service b23acc
/*****************************************************************************/
Packit Service b23acc
Packit Service b23acc
#endif /* __NM_DEDUP_MULTI_H__ */