369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 31a5eee990d64a98a071ce74cd4d53190c0fe94b Mon Sep 17 00:00:00 2001
 | |
| From: Eugene Syromyatnikov <evgsyr@gmail.com>
 | |
| Date: Sun, 11 Sep 2016 12:11:45 +0300
 | |
| Subject: [PATCH 26/27] 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.
 | |
| ---
 | |
|  Makefile.am |   1 +
 | |
|  list.h      | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | |
|  macros.h    |  19 ++++
 | |
|  3 files changed, 320 insertions(+)
 | |
|  create mode 100644 list.h
 | |
| 
 | |
| diff --git a/Makefile.am b/Makefile.am
 | |
| index f6679a3..f252bda 100644
 | |
| --- a/Makefile.am
 | |
| +++ b/Makefile.am
 | |
| @@ -165,6 +165,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		\
 | |
| diff --git a/list.h b/list.h
 | |
| new file mode 100644
 | |
| index 0000000..cf68fa7
 | |
| --- /dev/null
 | |
| +++ b/list.h
 | |
| @@ -0,0 +1,300 @@
 | |
| +/*
 | |
| + * Some simple implementation of lists similar to the one used in the kernel.
 | |
| + *
 | |
| + * Copyright (c) 2016-2019 The strace developers.
 | |
| + * All rights reserved.
 | |
| + *
 | |
| + * SPDX-License-Identifier: LGPL-2.1-or-later
 | |
| + */
 | |
| +
 | |
| +#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(const struct list_item *l)
 | |
| +{
 | |
| +	return ((l->next == l) && (l->prev == l))
 | |
| +		/*
 | |
| +		 * XXX This could be the case when struct list_item hasn't been
 | |
| +		 *     initialised at all; we should probably also call some
 | |
| +		 *     errror_func_msg() in that case, as it looks like sloppy
 | |
| +		 *     programming.
 | |
| +		 */
 | |
| +		|| (!l->next && !l->prev);
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * 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 the 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 the 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.
 | |
| + *
 | |
| + * @param item Pointer to struct list_item of the item to be removed.
 | |
| + * @return     Whether the action has been performed.
 | |
| + */
 | |
| +static inline bool
 | |
| +list_remove(struct list_item *item)
 | |
| +{
 | |
| +	if (!item->next || !item->prev || list_is_empty(item))
 | |
| +		return false;
 | |
| +
 | |
| +	item->prev->next = item->next;
 | |
| +	item->next->prev = item->prev;
 | |
| +	item->next = item->prev = item;
 | |
| +
 | |
| +	return true;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * 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.
 | |
| + *
 | |
| + * @param old Pointer to struct list_item of the item to be replaced.
 | |
| + * @param new Pointer to struct list_item of the item to be replaced with.
 | |
| + * @return    Whether the replacement has been performed.
 | |
| + */
 | |
| +static inline bool
 | |
| +list_replace(struct list_item *old, struct list_item *new)
 | |
| +{
 | |
| +	if (!old->next || !old->prev || list_is_empty(old))
 | |
| +		return false;
 | |
| +
 | |
| +	new->next = old->next;
 | |
| +	new->prev = old->prev;
 | |
| +	old->prev->next = new;
 | |
| +	old->next->prev = new;
 | |
| +	old->next = old->prev = old;
 | |
| +
 | |
| +	return true;
 | |
| +}
 | |
| +
 | |
| +/**
 | |
| + * 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 */
 | |
| diff --git a/macros.h b/macros.h
 | |
| index 9346d90..6951928 100644
 | |
| --- a/macros.h
 | |
| +++ b/macros.h
 | |
| @@ -33,6 +33,25 @@
 | |
|  	(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
 | |
| +/**
 | |
| + * Return a pointer to a structure that contains the provided variable.
 | |
| + *
 | |
| + * @param ptr_    Pointer to data that is a field of the container structure.
 | |
| + * @param struct_ Type of the container structure.
 | |
| + * @param member_ Name of the member field.
 | |
| + * @return  Pointer to the container structure.
 | |
| + */
 | |
| +# define containerof(ptr_, struct_, member_)	\
 | |
| +	cast_ptr(struct_ *,			\
 | |
| +		 (const volatile char *) (ptr_) - offsetof(struct_, member_))
 | |
| +#endif
 | |
| +
 | |
|  static inline bool
 | |
|  is_filled(const char *ptr, char fill, size_t size)
 | |
|  {
 | |
| -- 
 | |
| 2.1.4
 | |
| 
 |