/*** * Copyright 2009-2017 by Gabriel Parmer. All rights reserved. * Redistribution of this file is permitted under the BSD 2 clause license. * Author: Gabriel Parmer, gparmer@gwu.edu, 2017 * * History: * - Initial implementation, ~2009 * - Adapted for parsec and relicensed, 2016 */ /* * API Conventions: * - obj, new, and tmp are pointers of type T where T is the struct containing the linked list * - head is a pointer to struct ps_list_head * - l is the list field name within T * - type is T without any () * - all ps_list_head_* functions should be applied to struct ps_list_head pointers * - all ps_list_* functions should be passed items of type T, not struct ps_list_head * - as with most macro-based APIs, please avoid passing in functions that cannot be multiply evaluated; * generally passing only variables is a good move * * Example Usage: * * struct ps_list_head h; * struct foo { * struct ps_list l; * void *d; * } node, *i, *tmp; * * ps_list_head_init(&h); * ps_list_init(&node); * ps_list_head_add(&h, &node, l); * ... * for (i = ps_list_head_first(&h, struct foo, l) ; * i != ps_list_head(&h, struct foo, l) ; * i = ps_list_next(i, l)) { ... } * * for (ps_list_iter_init(&h, i, l) ; !ps_list_iter_term(&h, i, l) ; i = ps_list_next(i, l)) { ... } * * ps_list_foreach(&h, i, l) { ... } * * ps_list_foreach_del(&h, i, tmp, l) { * ps_list_rem(i, l); * ps_free(i); * } * */ #ifndef PS_LIST_H #define PS_LIST_H struct ps_list { struct ps_list *n, *p; }; /* * This is a separate type to 1) provide guidance on how to use the * API, and 2) to prevent developers from comparing pointers that * should not be compared. */ struct ps_list_head { struct ps_list l; }; #define PS_LIST_DEF_NAME list static inline void ps_list_ll_init(struct ps_list *l) { l->n = l->p = l; } static inline void ps_list_head_init(struct ps_list_head *lh) { ps_list_ll_init(&lh->l); } static inline int ps_list_ll_empty(struct ps_list *l) { return l->n == l; } static inline int ps_list_head_empty(struct ps_list_head *lh) { return ps_list_ll_empty(&lh->l); } static inline void ps_list_ll_add(struct ps_list *l, struct ps_list *new) { new->n = l->n; new->p = l; l->n = new; new->n->p = new; } static inline void ps_list_ll_rem(struct ps_list *l) { l->n->p = l->p; l->p->n = l->n; l->p = l->n = l; } #define ps_offsetof(s, field) __builtin_offsetof(s, field) //#define ps_offsetof(s, field) ((unsigned long)&(((s *)0)->field)) #define ps_container(intern, type, field) ((type *)((char *)(intern)-ps_offsetof(type, field))) /* * Get a pointer to the object containing *l, of a type shared with * *o. Importantly, "o" is not accessed here, and is _only_ used for * its type. It will typically be the iterator/cursor working through * a list. Do _not_ use this function. It is a utility used by the * following functions. */ #define ps_list_obj_get(l, o, lname) ps_container(l, __typeof__(*(o)), lname) //(typeof (*(o)) *)(((char*)(l)) - ps_offsetof(typeof(*(o)), lname)) /*** * The object API. These functions are called with pointers to your * own (typed) structures. */ #define ps_list_is_head(lh, o, lname) (ps_list_obj_get((lh), (o), lname) == (o)) /* functions for if we don't use the default name for the list field */ #define ps_list_singleton(o, lname) ps_list_ll_empty(&(o)->lname) #define ps_list_init(o, lname) ps_list_ll_init(&(o)->lname) #define ps_list_next(o, lname) ps_list_obj_get((o)->lname.n, (o), lname) #define ps_list_prev(o, lname) ps_list_obj_get((o)->lname.p, (o), lname) #define ps_list_add(o, n, lname) ps_list_ll_add(&(o)->lname, &(n)->lname) #define ps_list_append(o, n, lname) ps_list_add(ps_list_prev((o), lname), n, lname) #define ps_list_rem(o, lname) ps_list_ll_rem(&(o)->lname) #define ps_list_head_add(lh, o, lname) ps_list_ll_add((&(lh)->l), &(o)->lname) #define ps_list_head_append(lh, o, lname) ps_list_ll_add(((&(lh)->l)->p), &(o)->lname) /** * Explicit type API: Pass in the types of the nodes in the list, and * the name of the ps_list field in that type. */ #define ps_list_head_first(lh, type, lname) ps_container(((lh)->l.n), type, lname) #define ps_list_head_last(lh, type, lname) ps_container(((lh)->l.p), type, lname) /* If your struct named the list field "list" (as defined by PS_LIST_DEF_NAME */ #define ps_list_is_head_d(lh, o) ps_list_is_head(lh, o, PS_LIST_DEF_NAME) #define ps_list_singleton_d(o) ps_list_singleton(o, PS_LIST_DEF_NAME) #define ps_list_init_d(o) ps_list_init(o, PS_LIST_DEF_NAME) #define ps_list_next_d(o) ps_list_next(o, PS_LIST_DEF_NAME) #define ps_list_prev_d(o) ps_list_prev(o, PS_LIST_DEF_NAME) #define ps_list_add_d(o, n) ps_list_add(o, n, PS_LIST_DEF_NAME) #define ps_list_append_d(o, n) ps_list_append(o, n, PS_LIST_DEF_NAME) #define ps_list_rem_d(o) ps_list_rem(o, PS_LIST_DEF_NAME) #define ps_list_head_last_d(lh, o) ps_list_head_last(lh, o, PS_LIST_DEF_NAME) #define ps_list_head_first_d(lh, type) ps_list_head_first(lh, type, PS_LIST_DEF_NAME) #define ps_list_head_add_d(lh, o) ps_list_head_add(lh, o, PS_LIST_DEF_NAME) #define ps_list_head_append_d(lh, o) ps_list_head_append(lh, o, PS_LIST_DEF_NAME) /** * Iteration API */ /* Iteration without mutating the list */ #define ps_list_foreach(head, iter, lname) \ for (iter = ps_list_head_first((head), __typeof__(*iter), lname); !ps_list_is_head((head), iter, lname); \ (iter) = ps_list_next(iter, lname)) #define ps_list_foreach_d(head, iter) ps_list_foreach(head, iter, PS_LIST_DEF_NAME) /* * Iteration where the current node can be ps_list_rem'ed. * Notes: * - typeof(iter) == typeof(tmp) * - ps_list_add can be used on iter, but the added node will not be iterated over * * TODO: Add SMR/parallel version of this macro */ #define ps_list_foreach_del(head, iter, tmp, lname) \ for (iter = ps_list_head_first((head), __typeof__(*iter), lname), (tmp) = ps_list_next((iter), lname); \ !ps_list_is_head((head), iter, lname); (iter) = (tmp), (tmp) = ps_list_next((tmp), lname)) #define ps_list_foreach_del_d(head, iter, tmp) ps_list_foreach_del(head, iter, tmp, PS_LIST_DEF_NAME) #endif /* PS_LIST_H */