Add a generic list implementation
Similar to the one used in the Linux kernel. * macros.h (cast_ptr, containerof): New macros. * list.h: New file. * Makefile.am (strace_SOURCES): Add it.
This commit is contained in:
parent
c1838de552
commit
1bff0fadad
@ -168,6 +168,7 @@ strace_SOURCES = \
|
||||
linux/asm_stat.h \
|
||||
linux/x32/asm_stat.h \
|
||||
linux/x86_64/asm_stat.h \
|
||||
list.h \
|
||||
listen.c \
|
||||
lookup_dcookie.c \
|
||||
loop.c \
|
||||
|
295
list.h
Normal file
295
list.h
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Some simple implementation of lists similar to the one used in the kernel.
|
||||
*
|
||||
* Copyright (c) 2016-2018 The strace developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
*/
|
||||
|
||||
#ifndef STRACE_LIST_H
|
||||
#define STRACE_LIST_H
|
||||
|
||||
/*
|
||||
* struct list_item and related macros and functions provide an interface
|
||||
* for manipulating and iterating linked lists. In order to have list
|
||||
* associated with its payload, struct list_item has to be embedded into
|
||||
* a structure type representing payload, and (optionally) an additional
|
||||
* struct list_item should be added somewhere as a starting point for list
|
||||
* iteration (creating a list with a designated head). A situation where
|
||||
* no designated head exists, and each embedded struct list_head is considered
|
||||
* a head (i.e. starting point for list iteration), is also possible.
|
||||
*
|
||||
* List head has to be initialised with list_init() call. Statically allocated
|
||||
* list heads can also be defined with an EMPTY_LIST() macro.
|
||||
*
|
||||
* In order to get a pointer to list item from a struct list_item, list_elem
|
||||
* macro is used.
|
||||
*
|
||||
* When a designated head is used, list_head() and list_tail() can be used
|
||||
* for getting pointer to the first and the last list item, respectively.
|
||||
*
|
||||
* list_next() and list_prev() macros can be used for obtaining pointers
|
||||
* to the next and the previous items in the list, respectively. Note that
|
||||
* they do not perform additional checks for the validity of these pointers,
|
||||
* so they have to be guarded with respective list_head/list_tail checks in case
|
||||
* of lists with designated heads (where the list's head is not embedded withing
|
||||
* a list item.
|
||||
*
|
||||
* list_{insert,append,remove,remove_tail,remove_head,replace} provide some
|
||||
* basic means of list manipulation.
|
||||
*
|
||||
* list_foreach() and list_foreach_safe() are wrapper macros for simplifying
|
||||
* iteration over a list, with the latter having an additional argument
|
||||
* for storing temporary pointer, thus allowing list manipulations during
|
||||
* its iteration.
|
||||
*
|
||||
* A simple example:
|
||||
*
|
||||
* struct my_struct {
|
||||
* int a;
|
||||
* struct list_item l1;
|
||||
* struct list_item l2;
|
||||
* };
|
||||
*
|
||||
* EMPTY_LIST(list_1); <--- Defining a designated head for list
|
||||
*
|
||||
* struct my_struct *item =
|
||||
* calloc(1, sizeof(*item));
|
||||
* list_init(&item->l2); <--- Initialising structure field that
|
||||
* is used for lists without designated
|
||||
* head.
|
||||
* list_insert(&list_1, &item->l1); <--- Inserting an item into the list
|
||||
*
|
||||
* item = calloc(1, sizeof(*item));
|
||||
* list_init(&item->l2);
|
||||
*
|
||||
* list_append(&(list_head(list_1, struct my_struct, l1)->l2), &item->l2);
|
||||
*
|
||||
* struct my_struct *cur = item; <--- Iteration over a headless list
|
||||
* do {
|
||||
* printf("%d\n", cur->a);
|
||||
* } while ((cur = list_next(&cur, l2)) != item);
|
||||
*
|
||||
* struct my_struct *i;
|
||||
* list_foreach(i, list_1, l1) { <--- Iteration over list_1 without list
|
||||
* printf("%d\n", i->a); modification
|
||||
* }
|
||||
*
|
||||
* struct my_struct *tmp; <--- Iteration with modification
|
||||
* list_foreach_safe(i, list_1, l1, tmp) {
|
||||
* list_remove(&i->l1);
|
||||
* free(i);
|
||||
* }
|
||||
*
|
||||
* See also:
|
||||
* "Linux kernel design patterns - part 2", section "Linked Lists"
|
||||
* https://lwn.net/Articles/336255/
|
||||
*/
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
struct list_item {
|
||||
struct list_item *prev;
|
||||
struct list_item *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define an empty list head.
|
||||
*
|
||||
* @param l_ List head variable name.
|
||||
*/
|
||||
#define EMPTY_LIST(l_) struct list_item l_ = { &l_, &l_ }
|
||||
|
||||
/** Initialise an empty list. */
|
||||
static inline void
|
||||
list_init(struct list_item *l)
|
||||
{
|
||||
l->prev = l;
|
||||
l->next = l;
|
||||
}
|
||||
|
||||
/** Check whether list is empty. */
|
||||
static inline bool
|
||||
list_is_empty(struct list_item *l)
|
||||
{
|
||||
return (l->next == l) && (l->prev == l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a pointer to a struct list_item to a pointer to a list item.
|
||||
*
|
||||
* @param var Pointer to struct list_item.
|
||||
* @param type Type of the list's item.
|
||||
* @param field Name of the field that holds the respective struct list_item.
|
||||
*/
|
||||
#define list_elem(var, type, field) containerof((var), type, field)
|
||||
|
||||
/**
|
||||
* Get the first element in a list.
|
||||
*
|
||||
* @param head Pointer to the list's head.
|
||||
* @param type Type of the list's item.
|
||||
* @param field Name of the field that holds the respective struct list_item.
|
||||
*/
|
||||
#define list_head(head, type, field) \
|
||||
(list_is_empty(head) ? NULL : list_elem((head)->next, type, field))
|
||||
/**
|
||||
* Get the last element in a list.
|
||||
*
|
||||
* @param head Pointer to the list's head.
|
||||
* @param type Type of the list's item.
|
||||
* @param field Name of the field that holds the respective struct list_item.
|
||||
*/
|
||||
#define list_tail(head, type, field) \
|
||||
(list_is_empty(head) ? NULL : list_elem((head)->prev, type, field))
|
||||
|
||||
/**
|
||||
* Get the next element in a list.
|
||||
*
|
||||
* @param var Pointer to a list item.
|
||||
* @param field Name of the field that holds the respective struct list_item.
|
||||
*/
|
||||
#define list_next(var, field) \
|
||||
list_elem((var)->field.next, typeof(*(var)), field)
|
||||
/**
|
||||
* Get the previous element in a list.
|
||||
*
|
||||
* @param var Pointer to a list item.
|
||||
* @param field Name of the field that holds the respective struct list_item.
|
||||
*/
|
||||
#define list_prev(var, field) \
|
||||
list_elem((var)->field.prev, typeof(*(var)), field)
|
||||
|
||||
/**
|
||||
* Insert an item into a list. The item is placed as a next list item
|
||||
* to the head.
|
||||
*/
|
||||
static inline void
|
||||
list_insert(struct list_item *head, struct list_item *item)
|
||||
{
|
||||
item->next = head->next;
|
||||
item->prev = head;
|
||||
head->next->prev = item;
|
||||
head->next = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an item into a list. The item is placed as a previous list item
|
||||
* to the head.
|
||||
*/
|
||||
static inline void
|
||||
list_append(struct list_item *head, struct list_item *item)
|
||||
{
|
||||
item->next = head;
|
||||
item->prev = head->prev;
|
||||
head->prev->next = item;
|
||||
head->prev = item;
|
||||
}
|
||||
|
||||
/** Remove an item from a list. */
|
||||
static inline void
|
||||
list_remove(struct list_item *item)
|
||||
{
|
||||
if (!item->next || !item->prev)
|
||||
return;
|
||||
|
||||
item->prev->next = item->next;
|
||||
item->next->prev = item->prev;
|
||||
item->next = item->prev = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element of a list.
|
||||
*
|
||||
* @param head Pointer to the list's head.
|
||||
* @return Pointer to struct list_item removed from the list;
|
||||
* or NULL, if the list is empty.
|
||||
*/
|
||||
static inline struct list_item *
|
||||
list_remove_tail(struct list_item *head)
|
||||
{
|
||||
struct list_item *t = list_is_empty(head) ? NULL : head->prev;
|
||||
|
||||
if (t)
|
||||
list_remove(t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first element of a list.
|
||||
*
|
||||
* @param head Pointer to the list's head.
|
||||
* @return Pointer to struct list_item removed from the list;
|
||||
* or NULL, if the list is empty.
|
||||
*/
|
||||
static inline struct list_item *
|
||||
list_remove_head(struct list_item *head)
|
||||
{
|
||||
struct list_item *h = list_is_empty(head) ? NULL : head->next;
|
||||
|
||||
if (h)
|
||||
list_remove(h);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/** Replace an old struct list_item in a list with the new one. */
|
||||
static inline void
|
||||
list_replace(struct list_item *old, struct list_item *new)
|
||||
{
|
||||
new->next = old->next;
|
||||
new->prev = old->prev;
|
||||
old->prev->next = new;
|
||||
old->next->prev = new;
|
||||
old->next = old->prev = old;
|
||||
}
|
||||
|
||||
/**
|
||||
* List iteration wrapper for non-destructive operations.
|
||||
*
|
||||
* @param var_ Variable holding pointer to a current list item.
|
||||
* @param head_ Pointer to the list's head.
|
||||
* @param field_ Name of the field containing the respective struct list_item
|
||||
* inside list items.
|
||||
*/
|
||||
#define list_foreach(var_, head_, field_) \
|
||||
for (var_ = list_elem((head_)->next, typeof(*var_), field_); \
|
||||
&(var_->field_) != (head_); var_ = list_next(var_, field_))
|
||||
|
||||
/**
|
||||
* List iteration wrapper for destructive operations.
|
||||
*
|
||||
* @param var_ Variable holding pointer to a current list item.
|
||||
* @param head_ Pointer to the list's head.
|
||||
* @param field_ Name of the field containing the respective struct list_item
|
||||
* inside list items.
|
||||
* @param _tmp Temporary variable for storing pointer to the next item.
|
||||
*/
|
||||
#define list_foreach_safe(var_, head_, field_, _tmp) \
|
||||
for (var_ = list_elem((head_)->next, typeof(*var_), field_), \
|
||||
_tmp = list_elem((var_)->field_.next, typeof(*var_), field_); \
|
||||
&var_->field_ != head_; var_ = _tmp, _tmp = list_next(_tmp, field_))
|
||||
|
||||
#endif /* !STRACE_LIST_H */
|
9
macros.h
9
macros.h
@ -37,6 +37,15 @@
|
||||
(offsetof(type_, member_) + sizeof(((type_ *)0)->member_))
|
||||
# endif
|
||||
|
||||
#ifndef cast_ptr
|
||||
# define cast_ptr(type, var) ((type) (uintptr_t) (const volatile void *) (var))
|
||||
#endif
|
||||
|
||||
#ifndef containerof
|
||||
# define containerof(x, s, m) \
|
||||
cast_ptr(s *, (const volatile char *) (x) - offsetof(s, m))
|
||||
#endif
|
||||
|
||||
static inline bool
|
||||
is_filled(const char *ptr, char fill, size_t size)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user