450 lines
11 KiB
C
450 lines
11 KiB
C
|
/*-
|
||
|
* Copyright (c) 2017 Broadcom. All rights reserved.
|
||
|
* The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer.
|
||
|
*
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer in the documentation
|
||
|
* and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
||
|
* may be used to endorse or promote products derived from this software
|
||
|
* without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* $FreeBSD$
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @file
|
||
|
*
|
||
|
* OCS linked list API
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#if !defined(__OCS_LIST_H__)
|
||
|
#define __OCS_LIST_H__
|
||
|
|
||
|
#define OCS_LIST_DEBUG
|
||
|
|
||
|
#if defined(OCS_LIST_DEBUG)
|
||
|
|
||
|
|
||
|
#define ocs_list_magic_decl uint32_t magic;
|
||
|
#define OCS_LIST_LIST_MAGIC 0xcafe0000
|
||
|
#define OCS_LIST_LINK_MAGIC 0xcafe0001
|
||
|
#define ocs_list_set_list_magic list->magic = OCS_LIST_LIST_MAGIC
|
||
|
#define ocs_list_set_link_magic list->magic = OCS_LIST_LINK_MAGIC
|
||
|
|
||
|
#define ocs_list_assert(cond, ...) \
|
||
|
if (!(cond)) { \
|
||
|
return __VA_ARGS__; \
|
||
|
}
|
||
|
#else
|
||
|
#define ocs_list_magic_decl
|
||
|
#define ocs_list_assert(cond, ...)
|
||
|
#define ocs_list_set_list_magic
|
||
|
#define ocs_list_set_link_magic
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* @brief list/link structure
|
||
|
*
|
||
|
* used for both the list object, and the link object(s). offset
|
||
|
* is specified when the list is initialized; this implies that a list
|
||
|
* will always point to objects of the same type. offset is not used
|
||
|
* when ocs_list_t is used as a link (ocs_list_link_t).
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
typedef struct ocs_list_s ocs_list_t;
|
||
|
struct ocs_list_s {
|
||
|
ocs_list_magic_decl /*<< used if debugging is enabled */
|
||
|
ocs_list_t *next; /*<< pointer to head of list (or next if link) */
|
||
|
ocs_list_t *prev; /*<< pointer to tail of list (or previous if link) */
|
||
|
uint32_t offset; /*<< offset in bytes to the link element of the objects in list */
|
||
|
};
|
||
|
typedef ocs_list_t ocs_list_link_t;
|
||
|
|
||
|
/* item2link - return pointer to link given pointer to an item */
|
||
|
#define item2link(list, item) ((ocs_list_t*) (((uint8_t*)(item)) + (list)->offset))
|
||
|
|
||
|
/* link2item - return pointer to item given pointer to a link */
|
||
|
#define link2item(list, link) ((void*) (((uint8_t*)(link)) - (list)->offset))
|
||
|
|
||
|
/**
|
||
|
* @brief Initialize a list
|
||
|
*
|
||
|
* A list object is initialized. Helper define is used to call _ocs_list_init() with
|
||
|
* offsetof(type, link)
|
||
|
*
|
||
|
* @param list Pointer to list
|
||
|
* @param offset Offset in bytes in item to the link element
|
||
|
*
|
||
|
* @return none
|
||
|
*/
|
||
|
static inline void
|
||
|
_ocs_list_init(ocs_list_t *list, uint32_t offset)
|
||
|
{
|
||
|
ocs_list_assert(list);
|
||
|
ocs_list_set_list_magic;
|
||
|
|
||
|
list->next = list;
|
||
|
list->prev = list;
|
||
|
list->offset = offset;
|
||
|
}
|
||
|
#define ocs_list_init(head, type, link) _ocs_list_init(head, offsetof(type, link))
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Test if a list is empty
|
||
|
*
|
||
|
* @param list Pointer to list head
|
||
|
*
|
||
|
* @return 1 if empty, 0 otherwise
|
||
|
*/
|
||
|
static inline int32_t
|
||
|
ocs_list_empty(ocs_list_t *list)
|
||
|
{
|
||
|
ocs_list_assert(list, 1);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, 1);
|
||
|
return list->next == list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Test if a list is valid (ready for use)
|
||
|
*
|
||
|
* @param list Pointer to list head
|
||
|
*
|
||
|
* @return true if list is usable, false otherwise
|
||
|
*/
|
||
|
static inline int
|
||
|
ocs_list_valid(ocs_list_t *list)
|
||
|
{
|
||
|
return (list->magic == OCS_LIST_LIST_MAGIC);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Insert link between two other links
|
||
|
*
|
||
|
* Inserts a link in between two other links
|
||
|
*
|
||
|
* @param a Pointer to first link
|
||
|
* @param b Pointer to next link
|
||
|
* @param c Pointer to link to insert between a and b
|
||
|
*
|
||
|
* @return none
|
||
|
*/
|
||
|
static inline void
|
||
|
_ocs_list_insert_link(ocs_list_t *a, ocs_list_t *b, ocs_list_t *c)
|
||
|
{
|
||
|
ocs_list_assert(a);
|
||
|
ocs_list_assert((a->magic == OCS_LIST_LIST_MAGIC) || (a->magic == OCS_LIST_LINK_MAGIC));
|
||
|
ocs_list_assert(a->next);
|
||
|
ocs_list_assert(a->prev);
|
||
|
ocs_list_assert(b);
|
||
|
ocs_list_assert((b->magic == OCS_LIST_LIST_MAGIC) || (b->magic == OCS_LIST_LINK_MAGIC));
|
||
|
ocs_list_assert(b->next);
|
||
|
ocs_list_assert(b->prev);
|
||
|
ocs_list_assert(c);
|
||
|
ocs_list_assert((c->magic == OCS_LIST_LIST_MAGIC) || (c->magic == OCS_LIST_LINK_MAGIC));
|
||
|
ocs_list_assert(!c->next);
|
||
|
ocs_list_assert(!c->prev);
|
||
|
|
||
|
ocs_list_assert(a->offset == b->offset);
|
||
|
ocs_list_assert(b->offset == c->offset);
|
||
|
|
||
|
c->next = a->next;
|
||
|
c->prev = b->prev;
|
||
|
a->next = c;
|
||
|
b->prev = c;
|
||
|
}
|
||
|
|
||
|
#if defined(OCS_LIST_DEBUG)
|
||
|
/**
|
||
|
* @brief Initialize a list link for debug purposes
|
||
|
*
|
||
|
* For debugging a linked list link element has a magic number that is initialized,
|
||
|
* and the offset value initialzied and used for subsequent assertions.
|
||
|
*
|
||
|
*
|
||
|
* @param list Pointer to list head
|
||
|
* @param link Pointer to link to be initialized
|
||
|
*
|
||
|
* @return none
|
||
|
*/
|
||
|
static inline void
|
||
|
ocs_list_init_link(ocs_list_t *list, ocs_list_t *link)
|
||
|
{
|
||
|
ocs_list_assert(list);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC);
|
||
|
ocs_list_assert(link);
|
||
|
|
||
|
if (link->magic == 0) {
|
||
|
link->magic = OCS_LIST_LINK_MAGIC;
|
||
|
link->offset = list->offset;
|
||
|
link->next = NULL;
|
||
|
link->prev = NULL;
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#define ocs_list_init_link(...)
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Add an item to the head of the list
|
||
|
*
|
||
|
* @param list Pointer to list head
|
||
|
* @param item Item to add
|
||
|
*/
|
||
|
static inline void
|
||
|
ocs_list_add_head(ocs_list_t *list, void *item)
|
||
|
{
|
||
|
ocs_list_t *link;
|
||
|
|
||
|
ocs_list_assert(list);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC);
|
||
|
ocs_list_assert(item);
|
||
|
|
||
|
link = item2link(list, item);
|
||
|
ocs_list_init_link(list, link);
|
||
|
|
||
|
ocs_list_assert(link->magic == OCS_LIST_LINK_MAGIC);
|
||
|
ocs_list_assert(link->offset == list->offset);
|
||
|
ocs_list_assert(link->next == NULL);
|
||
|
ocs_list_assert(link->prev == NULL);
|
||
|
|
||
|
_ocs_list_insert_link(list, list->next, item2link(list, item));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Add an item to the tail of the list
|
||
|
*
|
||
|
* @param list Head of the list
|
||
|
* @param item Item to add
|
||
|
*/
|
||
|
static inline void
|
||
|
ocs_list_add_tail(ocs_list_t *list, void *item)
|
||
|
{
|
||
|
ocs_list_t *link;
|
||
|
|
||
|
ocs_list_assert(list);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC);
|
||
|
ocs_list_assert(item);
|
||
|
|
||
|
link = item2link(list, item);
|
||
|
ocs_list_init_link(list, link);
|
||
|
|
||
|
ocs_list_assert(link->magic == OCS_LIST_LINK_MAGIC);
|
||
|
ocs_list_assert(link->offset == list->offset);
|
||
|
ocs_list_assert(link->next == NULL);
|
||
|
ocs_list_assert(link->prev == NULL);
|
||
|
|
||
|
_ocs_list_insert_link(list->prev, list, link);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Return the first item in the list
|
||
|
*
|
||
|
* @param list Head of the list
|
||
|
*
|
||
|
* @return pointer to the first item, NULL otherwise
|
||
|
*/
|
||
|
static inline void *
|
||
|
ocs_list_get_head(ocs_list_t *list)
|
||
|
{
|
||
|
ocs_list_assert(list, NULL);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, NULL);
|
||
|
return ocs_list_empty(list) ? NULL : link2item(list, list->next);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Return the first item in the list
|
||
|
*
|
||
|
* @param list head of the list
|
||
|
*
|
||
|
* @return pointer to the last item, NULL otherwise
|
||
|
*/
|
||
|
static inline void *
|
||
|
ocs_list_get_tail(ocs_list_t *list)
|
||
|
{
|
||
|
ocs_list_assert(list, NULL);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, NULL);
|
||
|
return ocs_list_empty(list) ? NULL : link2item(list, list->prev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Return the last item in the list
|
||
|
*
|
||
|
* @param list Pointer to list head
|
||
|
*
|
||
|
* @return pointer to the last item, NULL otherwise
|
||
|
*/
|
||
|
static inline void *ocs_list_tail(ocs_list_t *list)
|
||
|
{
|
||
|
ocs_list_assert(list, NULL);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, NULL);
|
||
|
return ocs_list_empty(list) ? NULL : link2item(list, list->prev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Get the next item on the list
|
||
|
*
|
||
|
* @param list head of the list
|
||
|
* @param item current item
|
||
|
*
|
||
|
* @return pointer to the next item, NULL otherwise
|
||
|
*/
|
||
|
static inline void *ocs_list_next(ocs_list_t *list, void *item)
|
||
|
{
|
||
|
ocs_list_t *link;
|
||
|
|
||
|
if (item == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ocs_list_assert(list, NULL);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, NULL);
|
||
|
ocs_list_assert(item, NULL);
|
||
|
|
||
|
link = item2link(list, item);
|
||
|
|
||
|
ocs_list_assert(link->magic == OCS_LIST_LINK_MAGIC, NULL);
|
||
|
ocs_list_assert(link->offset == list->offset, NULL);
|
||
|
ocs_list_assert(link->next, NULL);
|
||
|
ocs_list_assert(link->prev, NULL);
|
||
|
|
||
|
if ((link->next) == list) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return link2item(list, link->next);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Remove and return an item from the head of the list
|
||
|
*
|
||
|
* @param list head of the list
|
||
|
*
|
||
|
* @return pointer to returned item, or NULL if list is empty
|
||
|
*/
|
||
|
#define ocs_list_remove_head(list) ocs_list_remove(list, ocs_list_get_head(list))
|
||
|
|
||
|
/**
|
||
|
* @ingroup os
|
||
|
* @brief Remove an item from the list
|
||
|
*
|
||
|
* @param list Head of the list
|
||
|
* @param item Item to remove
|
||
|
*
|
||
|
* @return pointer to item, or NULL if item is not found.
|
||
|
*/
|
||
|
static inline void *ocs_list_remove(ocs_list_t *list, void *item)
|
||
|
{
|
||
|
ocs_list_t *link;
|
||
|
ocs_list_t *prev;
|
||
|
ocs_list_t *next;
|
||
|
|
||
|
if (item == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
ocs_list_assert(list, NULL);
|
||
|
ocs_list_assert(list->magic == OCS_LIST_LIST_MAGIC, NULL);
|
||
|
|
||
|
link = item2link(list, item);
|
||
|
|
||
|
ocs_list_assert(link->magic == OCS_LIST_LINK_MAGIC, NULL);
|
||
|
ocs_list_assert(link->offset == list->offset, NULL);
|
||
|
ocs_list_assert(link->next, NULL);
|
||
|
ocs_list_assert(link->prev, NULL);
|
||
|
|
||
|
prev = link->prev;
|
||
|
next = link->next;
|
||
|
|
||
|
prev->next = next;
|
||
|
next->prev = prev;
|
||
|
|
||
|
link->next = link->prev = NULL;
|
||
|
|
||
|
return item;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Iterate a linked list
|
||
|
*
|
||
|
* Iterate a linked list.
|
||
|
*
|
||
|
* @param list Pointer to list
|
||
|
* @param item Pointer to iterated item
|
||
|
*
|
||
|
* note, item is NULL after full list is traversed.
|
||
|
|
||
|
* @return none
|
||
|
*/
|
||
|
|
||
|
#define ocs_list_foreach(list, item) \
|
||
|
for (item = ocs_list_get_head((list)); item; item = ocs_list_next((list), item) )
|
||
|
|
||
|
/**
|
||
|
* @brief Iterate a linked list safely
|
||
|
*
|
||
|
* Iterate a linked list safely, meaning that the iterated item
|
||
|
* may be safely removed from the list.
|
||
|
*
|
||
|
* @param list Pointer to list
|
||
|
* @param item Pointer to iterated item
|
||
|
* @param nxt Pointer to saveed iterated item
|
||
|
*
|
||
|
* note, item is NULL after full list is traversed.
|
||
|
*
|
||
|
* @return none
|
||
|
*/
|
||
|
|
||
|
#define ocs_list_foreach_safe(list, item, nxt) \
|
||
|
for (item = ocs_list_get_head(list), nxt = item ? ocs_list_next(list, item) : NULL; item; \
|
||
|
item = nxt, nxt = ocs_list_next(list, item))
|
||
|
|
||
|
/**
|
||
|
* @brief Test if object is on a list
|
||
|
*
|
||
|
* Returns True if object is on a list
|
||
|
*
|
||
|
* @param link Pointer to list link
|
||
|
*
|
||
|
* @return returns True if object is on a list
|
||
|
*/
|
||
|
static inline int32_t
|
||
|
ocs_list_on_list(ocs_list_link_t *link)
|
||
|
{
|
||
|
return (link->next != NULL);
|
||
|
}
|
||
|
|
||
|
#endif /* __OCS_LIST_H__ */
|