Blob Blame History Raw
/*
 * Soft:        Keepalived is a failover program for the LVS project
 *              <www.linuxvirtualserver.org>. It monitor & manipulate
 *              a loadbalanced server pool using multi-layer checks.
 *
 * Part:        List structure manipulation.
 *
 * Author:      Alexandre Cassen, <acassen@linux-vs.org>
 *
 *              This program 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 General Public License for more details.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *
 * Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
 */

#include "config.h"

#include <stdio.h>

#include "list.h"
#include "memory.h"

/* Multiple list helpers functions */
list
alloc_mlist_r(void (*free_func) (void *), void (*dump_func) (FILE *, void *), size_t size)
{
	list new = (list) MALLOC(size * sizeof (struct _list));
	new->free = free_func;
	new->dump = dump_func;
	return new;
}

#ifdef _VRRP_FD_DEBUG_
void
dump_mlist(FILE *fp, list l, size_t size)
{
	element e;
	unsigned i;

	for (i = 0; i < size; i++) {
		for (e = LIST_HEAD(&l[i]); e; ELEMENT_NEXT(e))
			if (l->dump)
				(*l->dump) (fp, e->data);
	}
}
#endif

static void
free_melement(list l, void (*free_func) (void *))
{
	element e;
	element next;

	for (e = LIST_HEAD(l); e; e = next) {
		next = e->next;
		if (free_func)
			(*free_func) (e->data);
		FREE(e);
	}
}

void
free_mlist_r(list l, size_t size)
{
	size_t i;

	if (!l)
		return;

	for (i = 0; i < size; i++)
		free_melement(&l[i], l->free);
	FREE(l);
}

/* Simple list helpers functions */
list
alloc_list_r(void (*free_func) (void *), void (*dump_func) (FILE *fp, void *))
{
	return alloc_mlist_r(free_func, dump_func, 1);
}

static element
alloc_element(void)
{
	element new = (element) MALLOC(sizeof (struct _element));
	return new;
}

static inline void
__list_add(list l, element e)
{
	e->prev = l->tail;
	e->next = NULL;

	if (l->head == NULL)
		l->head = e;
	else
		l->tail->next = e;
	l->tail = e;
	l->count++;
}

void
list_add_r(list l, void *data)
{
	element e = alloc_element();

	e->data = data;

	__list_add(l, e);
}

void
list_add_head_r(list l, void *data)
{
	element e = alloc_element();

	e->data = data;

	e->next = l->head;
	e->prev = NULL;

	if (l->tail == NULL)
		l->tail = e;
	else
		l->head->prev = e;
	l->head = e;
	l->count++;
}

static inline void
__list_remove(list l, element e)
{
	if (e->prev)
		e->prev->next = e->next;
	else
		l->head = e->next;

	if (e->next)
		e->next->prev = e->prev;
	else
		l->tail = e->prev;

	l->count--;
}

void
list_remove_r(list l, element e)
{
	if (l->free)
		(*l->free) (e->data);

	__list_remove(l, e);
	FREE(e);
}

void
list_del_r(list l, void *data)
{
	element e;

	for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) {
		if (ELEMENT_DATA(e) == data) {
			list_remove_r(l, e);
			return;
		}
	}
}

void
list_transfer(element e, list l_from, list l_to)
{
	__list_remove(l_from, e);
	__list_add(l_to, e);
}

void *
list_element(list l, size_t num)
{
	element e = LIST_HEAD(l);
	size_t i = 0;

	/* fetch element number num */
	for (i = 0; i < num; i++) {
		if (!e)
			return NULL;

		ELEMENT_NEXT(e);
	}

	if (e)
		return ELEMENT_DATA(e);
	return NULL;
}

void
dump_list(FILE *fp, list l)
{
	element e;

	if (LIST_ISEMPTY(l))
		return;

	for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e))
		if (l->dump)
			(*l->dump) (fp, e->data);
}

static void
free_elements(list l)
{
	element e;
	element next;

	for (e = LIST_HEAD(l); e; e = next) {
		next = e->next;
		if (l->free)
			(*l->free) (e->data);
		l->count--;
		FREE(e);
	}
#if 0
	if (l->count)
		log_message(LOG_INFO, "free_elements left %d elements on the list", l->count);
#endif
}

void
free_list_elements_r(list l)
{
	free_elements(l);

	l->head = NULL;
	l->tail = NULL;
}

void
free_list_r(list *lp)
{
	list l = *lp;

	if (!l)
		return;

	/* Remove the caller's reference to the list */
	*lp = NULL;

	free_elements(l);
	FREE(l);
}

void
free_list_element_r(list l, element e)
{
	if (!l || !e)
		return;
	if (l->head == e)
		l->head = e->next;
	else
		e->prev->next = e->next;
	if (l->tail == e)
		l->tail = e->prev;
	else
		e->next->prev = e->prev;
	if (l->free)
		(*l->free) (e->data);
	l->count--;
	FREE(e);
}

void
free_list_data_r(list l, void *data)
{
	element e;

	for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) {
		if (ELEMENT_DATA(e) == data) {
			free_list_element_r(l, e);
			return;
		}
	}
}