| /* |
| * Copyright (c) 2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: qdf_slist.h |
| * |
| * A minimal, singly linked list implementation, with push front, pop front, and |
| * remove capabilities. These are all O(1) operations. |
| * |
| * In order to remove an item, a pointer to the previous item must be known. |
| * Thus, removing an item is most efficient when combined with |
| * qdf_slist_for_each_del(). For cases where you need efficient removal of an |
| * arbitrary list node without iteration, consider using the doubly linked list |
| * qdf_list instead. |
| */ |
| |
| #ifndef __QDF_SLIST_H |
| #define __QDF_SLIST_H |
| |
| #include "qdf_trace.h" |
| #include "qdf_util.h" |
| |
| #define __qdf_slist_poison ((void *)0xdeaddeaddeaddeadull) |
| |
| /** |
| * struct qdf_slist - a singly linked list |
| * @head: pointer to the head of the list |
| */ |
| struct qdf_slist { |
| struct qdf_slist_node *head; |
| }; |
| |
| /** |
| * struct qdf_slist_node - a singly linked list node |
| * @next: pointer to the next node in the list, NULL if there is none |
| */ |
| struct qdf_slist_node { |
| struct qdf_slist_node *next; |
| }; |
| |
| #define __qdf_slist_item(node, cursor, node_field) ({ \ |
| struct qdf_slist_node *__n = (node); \ |
| (__n ? qdf_container_of(__n, typeof(*(cursor)), node_field) : NULL); }) |
| |
| #define __qdf_slist_next_item(slist, cursor, node_field) \ |
| __qdf_slist_item(cursor ? (cursor)->node_field.next : \ |
| (slist)->head, cursor, node_field) |
| |
| /** |
| * qdf_slist_for_each - iterate over all of the items in @slist |
| * @slist: pointer to the qdf_slist to iterate over |
| * @cursor: cursor pointer of the list's item type, populated for each item |
| * @node_field: name of the qdf_slist_node field in the item's type |
| */ |
| #define qdf_slist_for_each(slist, cursor, node_field) \ |
| for (cursor = __qdf_slist_item((slist)->head, cursor, node_field); \ |
| cursor; \ |
| cursor = __qdf_slist_item((cursor)->node_field.next, \ |
| cursor, node_field)) |
| |
| /** |
| * qdf_slist_for_each_del - iterate over all of the items in @slist, |
| * allowing for the safe deletion of nodes during iteration |
| * @slist: pointer to the qdf_slist to iterate over |
| * @prev: cursor pointer, populated with the previous item |
| * @cursor: cursor pointer of the list's item type, populated for each item |
| * @node_field: name of the qdf_slist_node field in the item's type |
| */ |
| #define qdf_slist_for_each_del(slist, prev, cursor, node_field) \ |
| for (prev = NULL, \ |
| cursor = __qdf_slist_item((slist)->head, cursor, node_field); \ |
| cursor; \ |
| prev = __qdf_slist_next_item(slist, prev, node_field) == \ |
| cursor ? cursor : prev, \ |
| cursor = __qdf_slist_next_item(slist, prev, node_field)) |
| |
| /** |
| * qdf_slist_init() - initialize a qdf_slist |
| * @slist: the list to initialize |
| * |
| * Return: None |
| */ |
| static inline void qdf_slist_init(struct qdf_slist *slist) |
| { |
| slist->head = NULL; |
| } |
| |
| /** |
| * qdf_slist_deinit() - deinitialize a qdf_slist |
| * @slist: the list to deinitialize |
| * |
| * Return: None |
| */ |
| static inline void qdf_slist_deinit(struct qdf_slist *slist) |
| { |
| QDF_BUG(!slist->head); |
| slist->head = __qdf_slist_poison; |
| } |
| |
| /** |
| * qdf_slist_empty() - check if a qdf_slist is empty |
| * @slist: the list to check |
| * |
| * Return: true if @slist contains zero items |
| */ |
| static inline bool qdf_slist_empty(struct qdf_slist *slist) |
| { |
| return !slist->head; |
| } |
| |
| /** |
| * qdf_slist_push() - push an item into the front of a qdf_slist |
| * @slist: the list to push into |
| * @cursor: the item to push |
| * @node_field: name of the qdf_slist_node field in the item's type |
| * |
| * Return: None |
| */ |
| #define qdf_slist_push(slist, cursor, node_field) \ |
| __qdf_slist_push(slist, &(cursor)->node_field) |
| |
| static inline void |
| __qdf_slist_push(struct qdf_slist *slist, struct qdf_slist_node *node) |
| { |
| node->next = slist->head; |
| slist->head = node; |
| } |
| |
| /** |
| * qdf_slist_pop() - pop an item from the front of a qdf_slist |
| * @slist: the list to pop from |
| * @cursor: cursor pointer of the list's item type, not populated |
| * @node_field: name of the qdf_slist_node field in the item's type |
| * |
| * Return: pointer to the popped item, NULL if @slist was empty |
| */ |
| #define qdf_slist_pop(slist, cursor, node_field) \ |
| __qdf_slist_item(__qdf_slist_pop(slist), cursor, node_field) |
| |
| static inline struct qdf_slist_node *__qdf_slist_pop(struct qdf_slist *slist) |
| { |
| struct qdf_slist_node *node = slist->head; |
| |
| if (!node) |
| return NULL; |
| |
| slist->head = node->next; |
| node->next = __qdf_slist_poison; |
| |
| return node; |
| } |
| |
| /** |
| * qdf_slist_remove() - remove an item from a qdf_slist |
| * @slist: the list to remove from |
| * @prev: pointer to the item previous to the item to remove, NULL removes head |
| * @node_field: name of the qdf_slist_node field in the item's type |
| * |
| * Return: pointer to the removed item, NULL if none was removed |
| */ |
| #define qdf_slist_remove(slist, prev, node_field) \ |
| __qdf_slist_item(__qdf_slist_remove(slist, \ |
| prev ? &(prev)->node_field : NULL), prev, node_field) |
| |
| static inline struct qdf_slist_node * |
| __qdf_slist_remove(struct qdf_slist *slist, struct qdf_slist_node *prev) |
| { |
| struct qdf_slist_node *node; |
| |
| if (!prev) |
| return __qdf_slist_pop(slist); |
| |
| if (!prev->next) |
| return NULL; |
| |
| node = prev->next; |
| prev->next = node->next; |
| node->next = __qdf_slist_poison; |
| |
| return node; |
| } |
| |
| #endif /* __QDF_SLIST_H */ |
| |