Add two new macros, SLIST_CONCAT and LIST_CONCAT. Note in both the

queue.h header file and in the queue.3 manual page that they are O(n)
so should be used only in low-usage paths with short lists (otherwise
an STAILQ or TAILQ should be used).

Reviewed by: kib
This commit is contained in:
Kirk McKusick 2016-08-16 17:07:48 +00:00
parent 39f7cbe9ab
commit 65d3199746
2 changed files with 77 additions and 3 deletions

View File

@ -28,12 +28,13 @@
.\" @(#)queue.3 8.2 (Berkeley) 1/24/94 .\" @(#)queue.3 8.2 (Berkeley) 1/24/94
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd June 24, 2015 .Dd August 15, 2016
.Dt QUEUE 3 .Dt QUEUE 3
.Os .Os
.Sh NAME .Sh NAME
.Nm SLIST_CLASS_ENTRY , .Nm SLIST_CLASS_ENTRY ,
.Nm SLIST_CLASS_HEAD , .Nm SLIST_CLASS_HEAD ,
.Nm SLIST_CONCAT ,
.Nm SLIST_EMPTY , .Nm SLIST_EMPTY ,
.Nm SLIST_ENTRY , .Nm SLIST_ENTRY ,
.Nm SLIST_FIRST , .Nm SLIST_FIRST ,
@ -75,6 +76,7 @@
.Nm STAILQ_SWAP , .Nm STAILQ_SWAP ,
.Nm LIST_CLASS_ENTRY , .Nm LIST_CLASS_ENTRY ,
.Nm LIST_CLASS_HEAD , .Nm LIST_CLASS_HEAD ,
.Nm LIST_CONCAT ,
.Nm LIST_EMPTY , .Nm LIST_EMPTY ,
.Nm LIST_ENTRY , .Nm LIST_ENTRY ,
.Nm LIST_FIRST , .Nm LIST_FIRST ,
@ -125,6 +127,7 @@ lists and tail queues
.\" .\"
.Fn SLIST_CLASS_ENTRY "CLASSTYPE" .Fn SLIST_CLASS_ENTRY "CLASSTYPE"
.Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn SLIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn SLIST_CONCAT "SLIST_HEAD *head1" "SLIST_HEAD *head2" "TYPE" "SLIST_ENTRY NAME"
.Fn SLIST_EMPTY "SLIST_HEAD *head" .Fn SLIST_EMPTY "SLIST_HEAD *head"
.Fn SLIST_ENTRY "TYPE" .Fn SLIST_ENTRY "TYPE"
.Fn SLIST_FIRST "SLIST_HEAD *head" .Fn SLIST_FIRST "SLIST_HEAD *head"
@ -168,6 +171,7 @@ lists and tail queues
.\" .\"
.Fn LIST_CLASS_ENTRY "CLASSTYPE" .Fn LIST_CLASS_ENTRY "CLASSTYPE"
.Fn LIST_CLASS_HEAD "HEADNAME" "CLASSTYPE" .Fn LIST_CLASS_HEAD "HEADNAME" "CLASSTYPE"
.Fn LIST_CONCAT "LIST_HEAD *head1" "LIST_HEAD *head2" "TYPE" "LIST_ENTRY NAME"
.Fn LIST_EMPTY "LIST_HEAD *head" .Fn LIST_EMPTY "LIST_HEAD *head"
.Fn LIST_ENTRY "TYPE" .Fn LIST_ENTRY "TYPE"
.Fn LIST_FIRST "LIST_HEAD *head" .Fn LIST_FIRST "LIST_HEAD *head"
@ -249,6 +253,8 @@ Singly-linked lists add the following functionality:
.Bl -enum -compact -offset indent .Bl -enum -compact -offset indent
.It .It
O(n) removal of any entry in the list. O(n) removal of any entry in the list.
.It
O(n) concatenation of two lists.
.El .El
.Pp .Pp
Singly-linked tail queues add the following functionality: Singly-linked tail queues add the following functionality:
@ -296,6 +302,8 @@ Linked lists are the simplest of the doubly linked data structures.
They add the following functionality over the above: They add the following functionality over the above:
.Bl -enum -compact -offset indent .Bl -enum -compact -offset indent
.It .It
O(n) concatenation of two lists.
.It
They may be traversed backwards. They may be traversed backwards.
.El .El
However: However:
@ -401,6 +409,19 @@ evaluates to an initializer for the list
.Fa head . .Fa head .
.Pp .Pp
The macro The macro
.Nm SLIST_CONCAT
concatenates the list headed by
.Fa head2
onto the end of the one headed by
.Fa head1
removing all entries from the former.
Use of this macro should be avoided as it traverses the entirety of the
.Fa head1
list.
A singly-linked tail queue should be used if this macro is needed in
high-usage code paths or to operate on long lists.
.Pp
The macro
.Nm SLIST_EMPTY .Nm SLIST_EMPTY
evaluates to true if there are no elements in the list. evaluates to true if there are no elements in the list.
.Pp .Pp
@ -508,6 +529,9 @@ The macro
removes the element removes the element
.Fa elm .Fa elm
from the list. from the list.
Use of this macro should be avoided as it traverses the entire list.
A doubly-linked list should be used if this macro is needed in
high-usage code paths or to operate on long lists.
.Pp .Pp
The macro The macro
.Nm SLIST_SWAP .Nm SLIST_SWAP
@ -724,6 +748,9 @@ The macro
removes the element removes the element
.Fa elm .Fa elm
from the tail queue. from the tail queue.
Use of this macro should be avoided as it traverses the entire list.
A doubly-linked tail queue should be used if this macro is needed in
high-usage code paths or to operate on long tail queues.
.Pp .Pp
The macro The macro
.Nm STAILQ_SWAP .Nm STAILQ_SWAP
@ -823,6 +850,19 @@ evaluates to an initializer for the list
.Fa head . .Fa head .
.Pp .Pp
The macro The macro
.Nm LIST_CONCAT
concatenates the list headed by
.Fa head2
onto the end of the one headed by
.Fa head1
removing all entries from the former.
Use of this macro should be avoided as it traverses the entirety of the
.Fa head1
list.
A tail queue should be used if this macro is needed in
high-usage code paths or to operate on long lists.
.Pp
The macro
.Nm LIST_EMPTY .Nm LIST_EMPTY
evaluates to true if there are no elements in the list. evaluates to true if there are no elements in the list.
.Pp .Pp

View File

@ -76,6 +76,10 @@
* *
* For details on the use of these macros, see the queue(3) manual page. * For details on the use of these macros, see the queue(3) manual page.
* *
* Below is a summary of implemented functions where:
* + means the macro is available
* - means the macro is not available
* s means the macro is available but is slow (runs in O(n) time)
* *
* SLIST LIST STAILQ TAILQ * SLIST LIST STAILQ TAILQ
* _HEAD + + + + * _HEAD + + + +
@ -101,10 +105,10 @@
* _INSERT_BEFORE - + - + * _INSERT_BEFORE - + - +
* _INSERT_AFTER + + + + * _INSERT_AFTER + + + +
* _INSERT_TAIL - - + + * _INSERT_TAIL - - + +
* _CONCAT - - + + * _CONCAT s s + +
* _REMOVE_AFTER + - + - * _REMOVE_AFTER + - + -
* _REMOVE_HEAD + - + - * _REMOVE_HEAD + - + -
* _REMOVE + + + + * _REMOVE s + s +
* _SWAP + + + + * _SWAP + + + +
* *
*/ */
@ -183,6 +187,19 @@ struct { \
/* /*
* Singly-linked List functions. * Singly-linked List functions.
*/ */
#define SLIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \
if (curelm == NULL) { \
if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \
SLIST_INIT(head2); \
} else if (SLIST_FIRST(head2) != NULL) { \
while (SLIST_NEXT(curelm, field) != NULL) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_NEXT(curelm, field) = SLIST_FIRST(head2); \
SLIST_INIT(head2); \
} \
} while (0)
#define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_FIRST(head) ((head)->slh_first)
@ -447,6 +464,23 @@ struct { \
#define QMD_LIST_CHECK_PREV(elm, field) #define QMD_LIST_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */ #endif /* (_KERNEL && INVARIANTS) */
#define LIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \
if (curelm == NULL) { \
if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) { \
LIST_FIRST(head2)->field.le_prev = \
&LIST_FIRST((head1)); \
LIST_INIT(head2); \
} \
} else if (LIST_FIRST(head2) != NULL) { \
while (LIST_NEXT(curelm, field) != NULL) \
curelm = LIST_NEXT(curelm, field); \
LIST_NEXT(curelm, field) = LIST_FIRST(head2); \
LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field); \
LIST_INIT(head2); \
} \
} while (0)
#define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first) #define LIST_FIRST(head) ((head)->lh_first)