/// @file list_head.h /// @brief /// @copyright (c) 2014-2022 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stddef.h" /// @brief Structure used to implement the list_head data structure. typedef struct list_head { /// @brief The previous element. struct list_head *prev; /// @brief The subsequent element. struct list_head *next; } list_head; /// @brief Get the struct for this entry. /// @param ptr The &list_head pointer. /// @param type The type of the struct this is embedded in. /// @param member The name of the list_head within the struct. #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /// @brief Iterates over a list. /// @param pos The &list_head to use as a loop cursor. /// @param head The head for your list. #define list_for_each(pos, head) \ for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /// @brief Iterates over a list backwards. /// @param pos The &list_head to use as a loop cursor. /// @param head The head for your list. #define list_for_each_prev(pos, head) \ for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) /// @brief Iterates over a list safe against removal of list entry. /// @param pos The &list_head to use as a loop cursor. /// @param store Another &list_head to use as temporary storage. /// @param head The head for your list. #define list_for_each_safe(pos, store, head) \ for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ (pos) = (store), (store) = (pos)->next) /// @brief Iterates over a list. /// @param pos The &list_head to use as a loop cursor. /// @param head The head for your list. #define list_for_each_decl(pos, head) \ for (list_head * (pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /// @brief Initializes the list_head. /// @param head The head for your list. #define list_head_init(head) (head)->next = (head)->prev = (head) /// @brief Initializes the list_head. /// @param head The head for your list. #define list_head_size(head) \ ({ \ unsigned __list_head_size = 0; \ list_for_each_decl(it, head) __list_head_size += 1; \ __list_head_size; \ }) /// @brief Insert element l2 after l1. static inline void list_head_insert_after(list_head *l1, list_head *l2) { // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] list_head *l1_next = l1->next; // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] l1->next = l2; // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] l2->prev = l1; // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l1<-[Lb] l2->next = l1_next; // [La]->l1 La<-[l1]->l2 l1<-[l2]->Lb l2<-[Lb] l1_next->prev = l2; } /// @brief Insert element l2 before l1. static inline void list_head_insert_before(list_head *l1, list_head *l2) { // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] list_head *l1_prev = l1->prev; // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] l1_prev->next = l2; // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] l2->prev = l1_prev; // [La]->l2 La<-[l2]->l1 La<-[l1]->Lb l1<-[Lb] l2->next = l1; // [La]->l2 La<-[l2]->l1 l2<-[l1]->Lb l1<-[Lb] l1->prev = l2; } /// @brief Remove l from the list. /// @param l The element to remove. static inline void list_head_del(list_head *l) { // [La]->l La<-[l]->Lb l<-[Lb] // [La]->Lb La<-[l]->Lb l<-[Lb] l->prev->next = l->next; // [La]->Lb La<-[l]->Lb La<-[Lb] l->next->prev = l->prev; // [La]->Lb l<-[l]->l La<-[Lb] l->next = l->prev = l; } /// @brief Tests whether the given list is empty. /// @param head The list to check. /// @return 1 if empty, 0 otherwise. static inline int list_head_empty(list_head const *head) { return head->next == head; } /// Insert a new entry between two known consecutive entries. static inline void __list_add(list_head *new, list_head *prev, list_head *next) { // [prev]-> <-[new]-> <-[next] // [prev]-> <-[new]-> new<-[next] next->prev = new; // [prev]-> <-[new]->next new<-[next] new->next = next; // [prev]-> prev<-[new]->next new<-[next] new->prev = prev; // [prev]->new prev<-[new]->next new<-[next] prev->next = new; } /// @brief Insert element l2 before l1. static inline void list_head_add(list_head *new, list_head *head) { __list_add(new, head, head->next); } /// @brief Insert element l2 before l1. static inline void list_head_add_tail(list_head *new, list_head *head) { __list_add(new, head->prev, head); } /// @brief Removes an element from the list pointer, it's used when we have a possibly /// null list pointer and want to pop an element from it static inline list_head *list_head_pop(list_head *listp) { if (list_head_empty(listp)) return NULL; list_head *value = listp->next; list_head_del(listp->next); return value; } static inline list_head *list_head_front(list_head *listp) { return listp->next; } /// Merges the elements of l2, into the elements of l1. static inline void list_head_merge(list_head *l1, list_head *l2) { l1->prev->next = l2->next; l2->next->prev = l1->prev; l2->prev->next = l1; l1->prev = l2->prev; // Initialize the second list. list_head_init(l2); }