vlc/include/vlc_list.h

393 lines
12 KiB
C

/******************************************************************************
* vlc_list.h
******************************************************************************
* Copyright © 2018 Rémi Denis-Courmont
*
* This program 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.1 of the License, or
* (at your option) any later version.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_LIST_H
# define VLC_LIST_H 1
# include <stdalign.h>
# include <stdbool.h>
/**
* \defgroup list Linked lists
* \ingroup cext
* @{
* \file
* This provides convenience helpers for linked lists.
*/
/**
* Doubly-linked list node.
*
* This data structure provides a doubly-linked list node.
* It must be embedded in each member of a list as a node proper.
* It also serves as a list head in the object containing the list.
*/
struct vlc_list
{
struct vlc_list *prev;
struct vlc_list *next;
};
/**
* Static initializer for a list head.
*/
#define VLC_LIST_INITIALIZER(h) { h, h }
/**
* Initializes an empty list head.
*/
static inline void vlc_list_init(struct vlc_list *restrict head)
{
head->prev = head;
head->next = head;
}
/**
* Inserts an element in a list.
*
* \param node [out] Node pointer of the element to insert.
* \param prev Node pointer of the previous element.
* \param next Node pointer of the next element.
*/
static inline void vlc_list_add_between(struct vlc_list *restrict node,
struct vlc_list *prev,
struct vlc_list *next)
{
node->prev = prev;
node->next = next;
prev->next = node;
next->prev = node;
}
/**
* Inserts an element after another.
*
* \param node [out] Node pointer of the element to insert
* \param prev Node pointer of the previous element.
*/
static inline void vlc_list_add_after(struct vlc_list *restrict node,
struct vlc_list *prev)
{
vlc_list_add_between(node, prev, prev->next);
}
/**
* Inserts an element before another.
*
* \param node [out] Node pointer of the element to insert.
* \param next Node pointer of the next element.
*/
static inline void vlc_list_add_before(struct vlc_list *restrict node,
struct vlc_list *next)
{
vlc_list_add_between(node, next->prev, next);
}
/**
* Appends an element into a list.
*
* \param node [out] Node pointer of the element to append to the list.
* \param head Head pointer of the list to append the element to.
*/
static inline void vlc_list_append(struct vlc_list *restrict node,
struct vlc_list *head)
{
vlc_list_add_before(node, head);
}
/**
* Prepends an element into a list.
*
* \param node [out] Node pointer of the element to prepend to the list.
* \param head Head pointer of the list to prepend the element to.
*/
static inline void vlc_list_prepend(struct vlc_list *restrict node,
struct vlc_list *head)
{
vlc_list_add_after(node, head);
}
/**
* Removes an element from a list.
*
* \param node Node of the element to remove from a list.
* \warning The element must be inside a list.
* Otherwise the behaviour is undefined.
*/
static inline void vlc_list_remove(struct vlc_list *restrict node)
{
struct vlc_list *prev = node->prev;
struct vlc_list *next = node->next;
prev->next = next;
next->prev = prev;
}
/**
* Replaces an element with another one.
*
* \param original [in] Node pointer of the element to remove from the list.
* \param substitute [out] Node pointer of the replacement.
*/
static inline void vlc_list_replace(const struct vlc_list *original,
struct vlc_list *restrict substitute)
{
vlc_list_add_between(substitute, original->prev, original->next);
}
/**
* Checks if a list is empty.
*
* \param head [in] Head of the list to be checked.
*
* \retval false The list is not empty.
* \retval true The list is empty.
*
* \note Obviously the list must have been initialized.
* Otherwise, the behaviour is undefined.
*/
static inline bool vlc_list_is_empty(const struct vlc_list *head)
{
return head->next == head;
}
/**
* Checks if an element is first in a list.
*
* \param node [in] List node of the element.
* \param head [in] Head of the list to be checked.
*
* \retval false The element is not first (or is in another list).
* \retval true The element is first.
*/
static inline bool vlc_list_is_first(const struct vlc_list *node,
const struct vlc_list *head)
{
return node->prev == head;
}
/**
* Checks if an element is last in a list.
*
* \param node [in] List node of the element.
* \param head [in] Head of the list to be checked.
*
* \retval false The element is not last (or is in another list).
* \retval true The element is last.
*/
static inline bool vlc_list_is_last(const struct vlc_list *node,
const struct vlc_list *head)
{
return node->next == head;
}
/**
* List iterator.
*/
struct vlc_list_it
{
const struct vlc_list *head;
struct vlc_list *current;
struct vlc_list *next;
};
static inline
struct vlc_list_it vlc_list_it_start(struct vlc_list *head)
{
struct vlc_list *first = head->next;
struct vlc_list_it it = { head, first, first->next };
return it;
}
static inline
struct vlc_list_it vlc_list_it_start_const(const struct vlc_list *head)
{
struct vlc_list *first = head->next;
struct vlc_list_it it = { head, first, first->next };
return it;
}
static inline
struct vlc_list_it vlc_list_it_reverse_start(struct vlc_list *head)
{
struct vlc_list *first = head->prev;
struct vlc_list_it it = { head, first, first->prev };
return it;
}
static inline
struct vlc_list_it vlc_list_it_reverse_start_const(const struct vlc_list *head)
{
struct vlc_list *first = head->prev;
struct vlc_list_it it = { head, first, first->prev };
return it;
}
static inline bool vlc_list_it_continue(const struct vlc_list_it *restrict it)
{
return it->current != it->head;
}
static inline void vlc_list_it_next(struct vlc_list_it *restrict it)
{
struct vlc_list *next = it->next;
it->current = next;
it->next = next->next;
}
static inline void vlc_list_it_prev(struct vlc_list_it *restrict it)
{
struct vlc_list *next = it->next;
it->current = next;
it->next = next->prev;
}
/**
* List iteration macro.
*
* This macro iterates over all elements (excluding the head) of a list,
* in order from the first to the last.
*
* For each iteration, it sets the cursor variable to the current element.
*
* \param pos Cursor pointer variable identifier.
* \param head [in] Head pointer of the list to iterate.
* \param member Identifier of the member of the data type
* serving as list node.
* \note It it safe to delete the current item while iterating.
* It is however <b>not</b> safe to delete another item.
*/
#define vlc_list_foreach(pos, head, member) \
for (struct vlc_list_it vlc_list_it__##pos = vlc_list_it_start(head); \
vlc_list_it_continue(&(vlc_list_it__##pos)) \
&& ((pos) = container_of((vlc_list_it__##pos).current, \
typeof (*(pos)), member), true); \
vlc_list_it_next(&(vlc_list_it__##pos)))
#define vlc_list_foreach_const(pos, head, member) \
for (struct vlc_list_it vlc_list_it__##pos = vlc_list_it_start_const(head); \
vlc_list_it_continue(&(vlc_list_it__##pos)) \
&& ((pos) = container_of((vlc_list_it__##pos).current, \
const typeof (*(pos)), member), true); \
vlc_list_it_next(&(vlc_list_it__##pos)))
/**
* List iteration macro.
*
* This macro iterates over all elements (excluding the head) of a list,
* in reversed order from the first to the last.
*
* For each iteration, it sets the cursor variable to the current element.
*
* \param pos Cursor pointer variable identifier.
* \param head [in] Head pointer of the list to iterate.
* \param member Identifier of the member of the data type
* serving as list node.
* \note It it safe to delete the current item while iterating.
* It is however <b>not</b> safe to delete another item.
*/
#define vlc_list_reverse_foreach(pos, head, member) \
for (struct vlc_list_it vlc_list_it_##pos = vlc_list_it_reverse_start(head); \
vlc_list_it_continue(&(vlc_list_it_##pos)) \
&& ((pos) = container_of((vlc_list_it_##pos).current, \
typeof (*(pos)), member), true); \
vlc_list_it_prev(&(vlc_list_it_##pos)))
/**
* Converts a list node pointer to an element pointer.
*
* \param ptr list node pointer
* \param type list data element type name
* \param member list node member within the data element compound type
*/
#define vlc_list_entry(ptr, type, member) container_of(ptr, type, member)
static inline void *vlc_list_first_or_null(const struct vlc_list *head,
size_t offset)
{
if (vlc_list_is_empty(head))
return NULL;
return ((char *)(head->next)) - offset;
}
static inline void *vlc_list_last_or_null(const struct vlc_list *head,
size_t offset)
{
if (vlc_list_is_empty(head))
return NULL;
return ((char *)(head->prev)) - offset;
}
static inline void *vlc_list_prev_or_null(const struct vlc_list *head,
const struct vlc_list *node,
size_t offset)
{
if (vlc_list_is_first(node, head))
return NULL;
return ((char *)(node->prev)) - offset;
}
static inline void *vlc_list_next_or_null(const struct vlc_list *head,
const struct vlc_list *node,
size_t offset)
{
if (vlc_list_is_last(node, head))
return NULL;
return ((char *)(node->next)) - offset;
}
/**
* Gets the first element.
*
* \param head [in] Head of list whose last element to get.
*
* \return the first entry in a list or NULL if empty.
*/
#define vlc_list_first_entry_or_null(head, type, member) \
((type *)vlc_list_first_or_null(head, offsetof (type, member)))
/**
* Gets the last element.
*
* \param head [in] Head of list whose last element to get.
*
* \return the last entry in a list or NULL if empty.
*/
#define vlc_list_last_entry_or_null(head, type, member) \
((type *)vlc_list_last_or_null(head, offsetof (type, member)))
#define vlc_list_prev_entry_or_null(head, entry, type, member) \
((type *)vlc_list_prev_or_null(head, &(entry)->member, \
offsetof (type, member)))
#define vlc_list_next_entry_or_null(head, entry, type, member) \
((type *)vlc_list_next_or_null(head, &(entry)->member, \
offsetof (type, member)))
/** \todo Merging lists, splitting lists. */
/** @} */
#endif /* VLC_LIST_H */