summaryrefslogblamecommitdiffstats
path: root/src/include/ipxe/list.h
blob: 231383d19109d08e70081cef3486f74aefb7fca1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                    








                                                                

                           
                   
                   
 
                                                
                  
                              
                               
                                  


                               





                                                   
 






                                                       
 








                                                         
 

                                           
  















                                                            












                                                          

                                                                            



                                                                               
                                                         
                                                         

                                                         



                                        

                                                                             




                                                             
                                                         
                                                         

                                                         
 

                                                            
  

                                           
   

                                                          






                              
                                  



                                                                   

                                                        
 


                                                         
                     



                               
                                 
   

                                                               
 


                                                         

   

























































































































































                                                                               
                                    
  



                                                                 
   


                                                         

   












                                                                 











                                                                               

                                 


                                                                 

                                                                               

                                                                               


                                                                               
   

                                                  


                                                                 

                                                                               

                                                                               



                                                                               
                                                                             
  



                                                                  

                                                                               

                                                                               




                                                                               
   































                                                            





                                                                 

                                                                               

                     
                         
#ifndef _IPXE_LIST_H
#define _IPXE_LIST_H

/** @file
 *
 * Linked lists
 *
 * This linked list handling code is based on the Linux kernel's
 * list.h.
 */

FILE_LICENCE ( GPL2_ONLY );

#include <stddef.h>
#include <assert.h>

/** A doubly-linked list entry (or list head) */
struct list_head {
	/** Next list entry */
	struct list_head *next;
	/** Previous list entry */
	struct list_head *prev;
};

/**
 * Initialise a static list head
 *
 * @v list		List head
 */
#define LIST_HEAD_INIT( list ) { &(list), &(list) }

/**
 * Declare a static list head
 *
 * @v list		List head
 */
#define LIST_HEAD( list ) \
	struct list_head list = LIST_HEAD_INIT ( list )

/**
 * Initialise a list head
 *
 * @v list		List head
 */
#define INIT_LIST_HEAD( list ) do {			\
	(list)->next = (list);				\
	(list)->prev = (list);				\
	} while ( 0 )

/**
 * Check a list entry or list head is valid
 *
 * @v list		List entry or head
 */
#define list_check( list ) ( {				\
	assert ( (list) != NULL );			\
	assert ( (list)->prev != NULL );		\
	assert ( (list)->next != NULL );		\
	assert ( (list)->next->prev == (list) );	\
	assert ( (list)->prev->next == (list) );	\
	} )

/**
 * Insert a list entry between two known consecutive entries
 *
 * @v new		New list entry
 * @v prev		Previous list entry
 * @v next		Next list entry
 */
static inline void __list_add ( struct list_head *new,
				struct list_head *prev,
				struct list_head *next ) {
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

/**
 * Add a new entry to the head of a list
 *
 * @v new		New entry to be added
 * @v head		List head, or entry after which to add the new entry
 */
static inline void list_add ( struct list_head *new, struct list_head *head ) {
	__list_add ( new, head, head->next );
}
#define list_add( new, head ) do {			\
	list_check ( (head) );				\
	list_add ( (new), (head) );			\
	} while ( 0 )

/**
 * Add a new entry to the tail of a list
 *
 * @v new		New entry to be added
 * @v head		List head, or entry before which to add the new entry
 */
static inline void list_add_tail ( struct list_head *new,
				   struct list_head *head ) {
	__list_add ( new, head->prev, head );
}
#define list_add_tail( new, head ) do {			\
	list_check ( (head) );				\
	list_add_tail ( (new), (head) );		\
	} while ( 0 )

/**
 * Delete a list entry between two known consecutive entries
 *
 * @v prev		Previous list entry
 * @v next		Next list entry
 */
static inline void __list_del ( struct list_head *prev,
				struct list_head *next ) {
	next->prev = prev;
	prev->next = next;
}

/**
 * Delete an entry from a list
 *
 * @v list		List entry
 *
 * Note that list_empty() on entry does not return true after this;
 * the entry is in an undefined state.
 */
static inline void list_del ( struct list_head *list ) {
	__list_del ( list->prev, list->next );
}
#define list_del( list ) do {				\
	list_check ( (list) );				\
	list_del ( (list) );				\
	} while ( 0 )

/**
 * Test whether a list is empty
 *
 * @v list		List head
 */
static inline int list_empty ( const struct list_head *list ) {
	return ( list->next == list );
}
#define list_empty( list ) ( {				\
	list_check ( (list) );				\
	list_empty ( (list) ); } )

/**
 * Test whether a list has just one entry
 *
 * @v list		List to test
 */
static inline int list_is_singular ( const struct list_head *list ) {
	return ( ( ! list_empty ( list ) ) && ( list->next == list->prev ) );
}
#define list_is_singular( list ) ( {			\
	list_check ( (list) );				\
	list_is_singular ( (list) ); } )

/**
 * Test whether an entry is the last entry in list
 *
 * @v list		List entry to test
 * @v head		List head
 */
static inline int list_is_last ( const struct list_head *list,
				 const struct list_head *head ) {
	return ( list->next == head );
}
#define list_is_last( list, head ) ( {			\
	list_check ( (list) );				\
	list_check ( (head) );				\
	list_is_last ( (list), (head) ); } )

/**
 * Cut a list into two
 *
 * @v new		A new list to contain all removed entries
 * @v list		An existing list
 * @v entry		An entry within the existing list
 *
 * All entries from @c list up to and including @c entry are moved to
 * @c new, which should be an empty list.  @c entry may be equal to @c
 * list, in which case no entries are moved.
 */
static inline void list_cut_position ( struct list_head *new,
				       struct list_head *list,
				       struct list_head *entry ) {
	struct list_head *first = entry->next;

	if ( list != entry ) {
		new->next = list->next;
		new->next->prev = new;
		new->prev = entry;
		new->prev->next = new;
		list->next = first;
		list->next->prev = list;
	}
}
#define list_cut_position( new, list, entry ) do {	\
	list_check ( (new) );				\
	assert ( list_empty ( (new) ) );		\
	list_check ( (list) );				\
	list_check ( (entry) );				\
	list_cut_position ( (new), (list), (entry) );	\
	} while ( 0 )

/**
 * Move all entries from one list into another list
 *
 * @v list		List of entries to add
 * @v entry		Entry after which to add the new entries
 *
 * All entries from @c list are inserted after @c entry.  Note that @c
 * list is left in an undefined state; use @c list_splice_init() if
 * you want @c list to become an empty list.
 */
static inline void list_splice ( const struct list_head *list,
				 struct list_head *entry ) {
	struct list_head *first = list->next;
	struct list_head *last = list->prev;

	if ( ! list_empty ( list ) ) {
		last->next = entry->next;
		last->next->prev = last;
		first->prev = entry;
		first->prev->next = first;
	}
}
#define list_splice( list, entry ) do {			\
	list_check ( (list) );				\
	list_check ( (entry) );				\
	list_splice ( (list), (entry) );		\
	} while ( 0 )

/**
 * Move all entries from one list into another list
 *
 * @v list		List of entries to add
 * @v entry		Entry before which to add the new entries
 *
 * All entries from @c list are inserted before @c entry.  Note that @c
 * list is left in an undefined state; use @c list_splice_tail_init() if
 * you want @c list to become an empty list.
 */
static inline void list_splice_tail ( const struct list_head *list,
				      struct list_head *entry ) {
	struct list_head *first = list->next;
	struct list_head *last = list->prev;

	if ( ! list_empty ( list ) ) {
		first->prev = entry->prev;
		first->prev->next = first;
		last->next = entry;
		last->next->prev = last;
	}
}
#define list_splice_tail( list, entry ) do {		\
	list_check ( (list) );				\
	list_check ( (entry) );				\
	list_splice_tail ( (list), (entry) );		\
	} while ( 0 )

/**
 * Move all entries from one list into another list and reinitialise empty list
 *
 * @v list		List of entries to add
 * @v entry		Entry after which to add the new entries
 *
 * All entries from @c list are inserted after @c entry.
 */
static inline void list_splice_init ( struct list_head *list,
				      struct list_head *entry ) {
	list_splice ( list, entry );
	INIT_LIST_HEAD ( list );
}
#define list_splice_init( list, entry ) do {			\
	list_check ( (list) );				\
	list_check ( (entry) );				\
	list_splice_init ( (list), (entry) );		\
	} while ( 0 )

/**
 * Move all entries from one list into another list and reinitialise empty list
 *
 * @v list		List of entries to add
 * @v entry		Entry before which to add the new entries
 *
 * All entries from @c list are inserted before @c entry.
 */
static inline void list_splice_tail_init ( struct list_head *list,
					   struct list_head *entry ) {
	list_splice_tail ( list, entry );
	INIT_LIST_HEAD ( list );
}
#define list_splice_tail_init( list, entry ) do {	\
	list_check ( (list) );				\
	list_check ( (entry) );				\
	list_splice_tail_init ( (list), (entry) );	\
	} while ( 0 )

/**
 * Get the container of a list entry
 *
 * @v list		List entry
 * @v type		Containing type
 * @v member		Name of list field within containing type
 * @ret container	Containing object
 */
#define list_entry( list, type, member ) ( {		\
	list_check ( (list) );				\
	container_of ( list, type, member ); } )

/**
 * Get the container of the first entry in a list
 *
 * @v list		List head
 * @v type		Containing type
 * @v member		Name of list field within containing type
 * @ret first		First list entry, or NULL
 */
#define list_first_entry( list, type, member )		\
	( list_empty ( (list) ) ?			\
	  ( type * ) NULL :				\
	  list_entry ( (list)->next, type, member ) )

/**
 * Iterate over a list
 *
 * @v pos		Iterator
 * @v head		List head
 */
#define list_for_each( pos, head )					      \
	for ( list_check ( (head) ),					      \
	      pos = (head)->next;					      \
	      pos != (head);						      \
	      pos = (pos)->next )

/**
 * Iterate over entries in a list
 *
 * @v pos		Iterator
 * @v head		List head
 * @v member		Name of list field within iterator's type
 */
#define list_for_each_entry( pos, head, member )			      \
	for ( list_check ( (head) ),					      \
	      pos = list_entry ( (head)->next, typeof ( *pos ), member );     \
	      &pos->member != (head);					      \
	      pos = list_entry ( pos->member.next, typeof ( *pos ), member ) )

/**
 * Iterate over entries in a list in reverse order
 *
 * @v pos		Iterator
 * @v head		List head
 * @v member		Name of list field within iterator's type
 */
#define list_for_each_entry_reverse( pos, head, member )		      \
	for ( list_check ( (head) ),					      \
	      pos = list_entry ( (head)->prev, typeof ( *pos ), member );     \
	      &pos->member != (head);					      \
	      pos = list_entry ( pos->member.prev, typeof ( *pos ), member ) )

/**
 * Iterate over entries in a list, safe against deletion of the current entry
 *
 * @v pos		Iterator
 * @v tmp		Temporary value (of same type as iterator)
 * @v head		List head
 * @v member		Name of list field within iterator's type
 */
#define list_for_each_entry_safe( pos, tmp, head, member )		      \
	for ( list_check ( (head) ),					      \
	      pos = list_entry ( (head)->next, typeof ( *pos ), member ),     \
	      tmp = list_entry ( pos->member.next, typeof ( *tmp ), member ); \
	      &pos->member != (head);					      \
	      pos = tmp,						      \
	      tmp = list_entry ( tmp->member.next, typeof ( *tmp ), member ) )

/**
 * Test if list contains a specified entry
 *
 * @v entry		Entry
 * @v head		List head
 * @ret present		List contains specified entry
 */
static inline int list_contains ( struct list_head *entry,
				  struct list_head *head ) {
	struct list_head *tmp;

	list_for_each ( tmp, head ) {
		if ( tmp == entry )
			return 1;
	}
	return 0;
}
#define list_contains( entry, head ) ( {		\
	list_check ( (head) );				\
	list_check ( (entry) );				\
	list_contains ( (entry), (head) ); } )

/**
 * Test if list contains a specified entry
 *
 * @v entry		Entry
 * @v head		List head
 * @ret present		List contains specified entry
 */
#define list_contains_entry( entry, head, member )	\
	list_contains ( &(entry)->member, (head) )

/**
 * Check list contains a specified entry
 *
 * @v entry		Entry
 * @v head		List head
 * @v member		Name of list field within iterator's type
 */
#define list_check_contains_entry( entry, head, member ) do {		      \
	assert ( list_contains_entry ( (entry), (head), member ) );	      \
	} while ( 0 )

#endif /* _IPXE_LIST_H */