[RFC][PATCH] type safe list

Vladimir V. Saveliev
2005-07-22T13:59:40Z

[-- Attachment #1: Type: text/plain, Size: 410 bytes --]

Hello

This is implementaion of circular doubly linked parametrized list.
It is similar to the list implementation in include/linux/list.h
but it also provides type safety which allows to detect some of list manipulating
mistakes as soon as they happen.

Please consider how feasible is a chance to have this patch accepted.
These lists are widely used by reiser4. I believe others may find it useful.

Thanks

[-- Attachment #2: type-safe-list.patch --]
[-- Type: text/plain, Size: 18051 bytes --]

This is implementaion of circular doubly linked parametrized list.
It is similar to the list implementation in include/linux/list.h
but it also provides type safety which allows to detect some of list manipulating
mistakes as soon as they happen.

include/linux/type_safe_list.h | 433 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 433 insertions(+)

diff -puN /dev/null include/linux/type_safe_list.h
--- /dev/null 2003-09-23 21:59:22.000000000 +0400
+++ linux-2.6.12-vs/include/linux/type_safe_list.h 2005-07-14 19:28:46.839464437 +0400
@@ -0,0 +1,433 @@
+#ifndef _LINUX_TYPE_SAFE_LIST_H
+#define _LINUX_TYPE_SAFE_LIST_H
+
+/* A circular doubly linked list that differs from the previous
+ <linux/list.h> implementation because it is parametrized to provide
+ type safety. This data structure is also useful as a queue or stack.
+
+ The "list template" consists of a set of types and methods for
+ implementing list operations. All of the types and methods
+ associated with a single list class are assigned unique names and
+ type signatures, thus allowing the compiler to verify correct
+ usage.
+
+ The first parameter of a list class is the item type being stored
+ in the list. The list class maintains two pointers within each
+ item structure for its "next" and "prev" pointers.
+
+ There are two structures associated with the list, in addition to
+ the item type itself. The "list link" contains the two pointers
+ that are embedded within the item itself. The "list head" also
+ contains two pointers which refer to the first item ("front") and
+ last item ("back") of the list.
+
+ The list maintains a "circular" invariant, in that you can always
+ begin at the front and follow "next" pointers until eventually you
+ reach the same point. The "list head" is included within the
+ cycle, even though it does not have the correct item type. The
+ "list head" and "list link" types are different objects from the
+ user's perspective, but the core algorithms that operate on this
+ style of list treat the "list head" and "list link" as identical
+ types. That is why these algorithms are so simple.
+
+ The <linux/list.h> implementation uses the same algorithms as those
+ in this file but uses only a single type "struct list_head". There
+ are two problems with this approach. First, there are no type
+ distinctions made between the two objects despite their distinct
+ types, which greatly increases the possibility for mistakes. For
+ example, the <linux/list.h> list_add function takes two "struct
+ list_head" arguments: the first is the item being inserted and the
+ second is the "struct list_head" which should precede the new
+ insertion to the list. You can use this function to insert at any
+ point in the list, but by far the most common list operations are
+ to insert at the front or back of the list. This common case
+ should accept two different argument types: a "list head" and an
+ "item", this allows for no confusion.
+
+ The second problem with using a single "struct list_head" is that
+ it does not distinguish between list objects of distinct list
+ classes. If a single item can belong to two separate lists, there
+ is easily the possibility of a mistake being made that causes the
+ item to be added to a "list head" using the wrong "list link". By
+ using a parametrized list class we can statically detect such
+ mistakes, detecting mistakes as soon as they happen.
+
+ To create a new list class takes several steps which are described
+ below. Suppose for this example that you would like to link
+ together items of type "rx_event". You should decide on
+ prefix-name to be used on all list functions and structures. For
+ example, the string "rx_event" can be as a prefix for all the list
+ operations, resulting in a "list head" named rx_event_list_head and
+ a "list link" named rx_event_list_link. The list operations on
+ this list class would be named "rx_event_list_empty",
+ "rx_event_list_init", "rx_event_list_push_front",
+ "rx_event_list_push_back", and so on.
+*/
+
+#define TYPE_SAFE_LIST_LINK_INIT(name) { &(name), &(name) }
+#define TYPE_SAFE_LIST_HEAD_INIT(name) { (void *)&(name), (void *)&(name) }
+#define TYPE_SAFE_LIST_LINK_ZERO { NULL, NULL }
+#define TYPE_SAFE_LIST_HEAD_ZERO { NULL, NULL }
+
+#define TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,LINK) \
+ ((ITEM_TYPE *)((char *)(LINK)-(unsigned long)(&((ITEM_TYPE *)0)->LINK_NAME)))
+
+/* Step 1: Use the TYPE_SAFE_LIST_DECLARE() macro to define the "list head"
+ and "list link" objects. This macro takes one arguments, the
+ prefix-name, which is prepended to every structure and function
+ name of the list class. Following the example, this will create
+ types named rx_event_list_head and rx_event_list_link. In the
+ example you would write:
+
+ TYPE_SAFE_LIST_DECLARE(rx_event);
+
+*/
+#define TYPE_SAFE_LIST_DECLARE(PREFIX) \
+ \
+typedef struct _##PREFIX##_list_head PREFIX##_list_head; \
+typedef struct _##PREFIX##_list_link PREFIX##_list_link; \
+ \
+struct _##PREFIX##_list_link \
+{ \
+ PREFIX##_list_link *_next; \
+ PREFIX##_list_link *_prev; \
+}; \
+ \
+struct _##PREFIX##_list_head \
+{ \
+ PREFIX##_list_link *_next; \
+ PREFIX##_list_link *_prev; \
+}
+
+/* Step 2: Once you have defined the two list classes, you should
+ define the item type you intend to use. The list classes must be
+ declared before the item type because the item type must contain an
+ embedded "list link" object. Following the example, you might define
+ rx_event as follows:
+
+ typedef struct _rx_event rx_event;
+
+ struct _rx_event
+ {
+ ... other members ...
+
+ rx_event_list_link _link;
+ };
+
+ In this case we have given the rx_event a field named "_link" of
+ the appropriate type.
+*/
+
+/* Step 3: The final step will define the list-functions for a
+ specific list class using the macro TYPE_SAFE_LIST_DEFINE. There are
+ three arguments to the TYPE_SAFE_LIST_DEFINE macro: the prefix-name, the
+ item type name, and field name of the "list link" element within
+ the item type. In the above example you would supply "rx_event" as
+ the type name and "_link" as the field name (without quotes).
+ E.g.,
+
+ TYPE_SAFE_LIST_DEFINE(rx_event,rx_event,_link)
+
+ The list class you define is now complete with the functions:
+
+ rx_event_list_init Initialize a list_head
+ rx_event_list_clean Initialize a list_link
+ rx_event_list_is_clean True if list_link is not in a list
+ rx_event_list_push_front Insert to the front of the list
+ rx_event_list_push_back Insert to the back of the list
+ rx_event_list_insert_before Insert just before given item in the list
+ rx_event_list_insert_after Insert just after given item in the list
+ rx_event_list_remove Remove an item from anywhere in the list
+ rx_event_list_remove_clean Remove an item from anywhere in the list and clean link_item
+ rx_event_list_remove_get_next Remove an item from anywhere in the list and return the next element
+ rx_event_list_remove_get_prev Remove an item from anywhere in the list and return the prev element
+ rx_event_list_pop_front Remove and return the front of the list, cannot be empty
+ rx_event_list_pop_back Remove and return the back of the list, cannot be empty
+ rx_event_list_front Get the front of the list
+ rx_event_list_back Get the back of the list
+ rx_event_list_next Iterate front-to-back through the list
+ rx_event_list_prev Iterate back-to-front through the list
+ rx_event_list_end Test to end an iteration, either direction
+ rx_event_list_splice Join two lists at the head
+ rx_event_list_empty True if the list is empty
+ rx_event_list_object_ok Check that list element satisfies double
+ list invariants. For debugging.
+
+ To iterate over such a list use a for-loop such as:
+
+ rx_event_list_head *head = ...;
+ rx_event *item;
+
+ for (item = rx_event_list_front (head);
+ ! rx_event_list_end (head, item);
+ item = rx_event_list_next (item))
+ {...}
+*/
+#define TYPE_SAFE_LIST_DEFINE(PREFIX,ITEM_TYPE,LINK_NAME) \
+ \
+static __inline__ int \
+PREFIX##_list_link_invariant (const PREFIX##_list_link *_link) \
+{ \
+ return (_link != NULL) && \
+ (_link->_prev != NULL) && (_link->_next != NULL ) && \
+ (_link->_prev->_next == _link) && \
+ (_link->_next->_prev == _link); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_link_ok (const PREFIX##_list_link *_link UNUSED_ARG) \
+{ \
+ BUG_ON(!PREFIX##_list_link_invariant (_link)); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_object_ok (const ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_link_ok (&item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_init (PREFIX##_list_head *head) \
+{ \
+ head->_next = (PREFIX##_list_link*) head; \
+ head->_prev = (PREFIX##_list_link*) head; \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_clean (ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_link *_link = &item->LINK_NAME; \
+ \
+ _link->_next = _link; \
+ _link->_prev = _link; \
+} \
+ \
+static __inline__ int \
+PREFIX##_list_is_clean (const ITEM_TYPE *item) \
+{ \
+ const PREFIX##_list_link *_link = &item->LINK_NAME; \
+ \
+ PREFIX##_list_link_ok (_link); \
+ return (_link == _link->_next) && (_link == _link->_prev); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_insert_int (PREFIX##_list_link *next, \
+ PREFIX##_list_link *item) \
+{ \
+ PREFIX##_list_link *prev = next->_prev; \
+ PREFIX##_list_link_ok (next); \
+ PREFIX##_list_link_ok (prev); \
+ next->_prev = item; \
+ item->_next = next; \
+ item->_prev = prev; \
+ prev->_next = item; \
+ PREFIX##_list_link_ok (next); \
+ PREFIX##_list_link_ok (prev); \
+ PREFIX##_list_link_ok (item); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_push_front (PREFIX##_list_head *head, \
+ ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_insert_int (head->_next, & item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_push_back (PREFIX##_list_head *head, \
+ ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_insert_int ((PREFIX##_list_link *) head, & item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_insert_before (ITEM_TYPE *reference, \
+ ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_insert_int (& reference->LINK_NAME, & item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_insert_after (ITEM_TYPE *reference, \
+ ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_insert_int (reference->LINK_NAME._next, & item->LINK_NAME); \
+} \
+ \
+static __inline__ PREFIX##_list_link* \
+PREFIX##_list_remove_int (PREFIX##_list_link *list_link) \
+{ \
+ PREFIX##_list_link *next = list_link->_next; \
+ PREFIX##_list_link *prev = list_link->_prev; \
+ PREFIX##_list_link_ok (list_link); \
+ PREFIX##_list_link_ok (next); \
+ PREFIX##_list_link_ok (prev); \
+ next->_prev = prev; \
+ prev->_next = next; \
+ PREFIX##_list_link_ok (next); \
+ PREFIX##_list_link_ok (prev); \
+ return list_link; \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_remove (ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_remove_int (& item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_remove_clean (ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_remove_int (& item->LINK_NAME); \
+ PREFIX##_list_clean (item); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_remove_get_next (ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_link *next = item->LINK_NAME._next; \
+ PREFIX##_list_remove_int (& item->LINK_NAME); \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,next); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_remove_get_prev (ITEM_TYPE *item) \
+{ \
+ PREFIX##_list_link *prev = item->LINK_NAME._prev; \
+ PREFIX##_list_remove_int (& item->LINK_NAME); \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,prev); \
+} \
+ \
+static __inline__ int \
+PREFIX##_list_empty (const PREFIX##_list_head *head) \
+{ \
+ return head == (PREFIX##_list_head*) head->_next; \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_pop_front (PREFIX##_list_head *head) \
+{ \
+ BUG_ON(PREFIX##_list_empty (head)); \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,PREFIX##_list_remove_int (head->_next)); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_pop_back (PREFIX##_list_head *head) \
+{ \
+ BUG_ON(PREFIX##_list_empty (head)); /* WWI started */ \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,PREFIX##_list_remove_int (head->_prev)); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_front (const PREFIX##_list_head *head) \
+{ \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,head->_next); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_back (const PREFIX##_list_head *head) \
+{ \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,head->_prev); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_next (const ITEM_TYPE *item) \
+{ \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,item->LINK_NAME._next); \
+} \
+ \
+static __inline__ ITEM_TYPE* \
+PREFIX##_list_prev (const ITEM_TYPE *item) \
+{ \
+ return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,item->LINK_NAME._prev); \
+} \
+ \
+static __inline__ int \
+PREFIX##_list_end (const PREFIX##_list_head *head, \
+ const ITEM_TYPE *item) \
+{ \
+ return ((PREFIX##_list_link *) head) == (& item->LINK_NAME); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_splice (PREFIX##_list_head *head_join, \
+ PREFIX##_list_head *head_empty) \
+{ \
+ if (PREFIX##_list_empty (head_empty)) { \
+ return; \
+ } \
+ \
+ head_empty->_prev->_next = (PREFIX##_list_link*) head_join; \
+ head_empty->_next->_prev = head_join->_prev; \
+ \
+ head_join->_prev->_next = head_empty->_next; \
+ head_join->_prev = head_empty->_prev; \
+ \
+ PREFIX##_list_link_ok ((PREFIX##_list_link*) head_join); \
+ PREFIX##_list_link_ok (head_join->_prev); \
+ PREFIX##_list_link_ok (head_join->_next); \
+ \
+ PREFIX##_list_init (head_empty); \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_split(PREFIX##_list_head *head_split, \
+ PREFIX##_list_head *head_new, \
+ ITEM_TYPE *item) \
+{ \
+ BUG_ON(!PREFIX##_list_empty(head_new)); \
+ \
+ /* attach to new list */ \
+ head_new->_next = (& item->LINK_NAME); \
+ head_new->_prev = head_split->_prev; \
+ \
+ /* cut from old list */ \
+ item->LINK_NAME._prev->_next = (PREFIX##_list_link*)head_split; \
+ head_split->_prev = item->LINK_NAME._prev; \
+ \
+ /* link new list */ \
+ head_new->_next->_prev = (PREFIX##_list_link*)head_new; \
+ head_new->_prev->_next = (PREFIX##_list_link*)head_new; \
+} \
+ \
+static __inline__ void \
+PREFIX##_list_check (const PREFIX##_list_head *head) \
+{ \
+ const PREFIX##_list_link *link; \
+ \
+ for (link = head->_next ; link != ((PREFIX##_list_link *) head) ; link = link->_next) \
+ PREFIX##_list_link_ok (link); \
+} \
+ \
+typedef struct { int foo; } PREFIX##_list_dummy_decl
+
+/* The final typedef is to allow a semicolon at the end of
+ * TYPE_SAFE_LIST_DEFINE(); */
+
+#define for_all_type_safe_list(prefix, head, item) \
+ for(item = prefix ## _list_front(head), \
+ prefetch(prefix ## _list_next(item)); \
+ !prefix ## _list_end(head, item) ; \
+ item = prefix ## _list_next(item), \
+ prefetch(prefix ## _list_next(item)))
+
+#define for_all_type_safe_list_safe(prefix, head, item, next) \
+ for(item = prefix ## _list_front(head), \
+ next = prefix ## _list_next(item); \
+ !prefix ## _list_end(head, item) ; \
+ item = next, \
+ next = prefix ## _list_next(item))
+
+/* _LINUX_TYPE_SAFE_LIST_H */
+#endif
+
+/*
+ Local variables:
+ c-indentation-style: "K&R"
+ mode-name: "LC"
+ c-basic-offset: 8
+ tab-width: 8
+ fill-column: 120
+ End:
+*/

_