GCQ_INIT(name
)
GCQ_INIT_HEAD(name
)
static
inline
void
gcq_init(struct gcq *q
)
static
inline
void
gcq_init_head(struct gcq_head *head
)
static
inline
struct
gcq
*
gcq_q(struct gcq_head *head
)
static
inline
struct
gcq
*
gcq_hq(struct gcq_head *head
)
static
inline
struct
gcq_head
*
gcq_head(struct gcq *q
)
static
inline
struct
gcq
*
gcq_remove(struct gcq *q
)
static
inline
bool
gcq_onlist(struct gcq *q
)
static
inline
bool
gcq_empty(struct gcq_head *head
)
static
inline
bool
gcq_linked(struct gcq *prev
, struct gcq *next
)
static
inline
void
gcq_insert_after(struct gcq *on
, struct gcq *off
)
static
inline
void
gcq_insert_before(struct gcq *on
, struct gcq *off
)
static
inline
void
gcq_insert_head(struct gcq_head *head
, struct gcq *q
)
static
inline
void
gcq_insert_tail(struct gcq_head *head
, struct gcq *q
)
static
inline
void
gcq_tie(struct gcq *dst
, struct gcq *src
)
static
inline
void
gcq_tie_after(struct gcq *dst
, struct gcq *src
)
static
inline
void
gcq_tie_before(struct gcq *dst
, struct gcq *src
)
static
inline
void
gcq_merge(struct gcq *dst
, struct gcq *src
)
static
inline
void
gcq_merge_tail(struct gcq_head *dst
, struct gcq_head *src
)
static
inline
void
gcq_merge_head(struct gcq_head *dst
, struct gcq_head *src
)
static
inline
void
gcq_clear(struct gcq *q
)
static
inline
void
gcq_remove_all(struct gcq_head *head
)
type
*
GCQ_ITEM(q
, type
, name
)
bool
GCQ_GOT_FIRST(var
, head
)
bool
GCQ_GOT_LAST(var
, head
)
bool
GCQ_GOT_NEXT(var
, current
, head
, start
)
bool
GCQ_GOT_PREV(var
, current
, head
, start
)
bool
GCQ_DEQUEUED_FIRST(var
, head
)
bool
GCQ_DEQUEUED_LAST(var
, head
)
bool
GCQ_DEQUEUED_NEXT(var
, current
, head
, start
)
bool
GCQ_DEQUEUED_PREV(var
, current
, head
, start
)
bool
GCQ_GOT_FIRST_TYPED(tvar
, head
, type
, name
)
bool
GCQ_GOT_LAST_TYPED(tvar
, head
, type
, name
)
bool
GCQ_GOT_NEXT_TYPED(tvar
, current
, head
, start
, type
, name
)
bool
GCQ_GOT_PREV_TYPED(tvar
, current
, head
, start
, type
, name
)
bool
GCQ_DEQUEUED_FIRST_TYPED(tvar
, head
, type
, name
)
bool
GCQ_DEQUEUED_LAST_TYPED(tvar
, head
, type
, name
)
bool
GCQ_DEQUEUED_NEXT_TYPED(tvar
, current
, head
, start
, type
, name
)
bool
GCQ_DEQUEUED_PREV_TYPED(tvar
, current
, head
, start
, type
, name
)
bool
GCQ_GOT_FIRST_COND(var
, head
, cond
)
bool
GCQ_GOT_LAST_COND(var
, head
, cond
)
bool
GCQ_GOT_NEXT_COND(var
, current
, head
, start
, cond
)
bool
GCQ_GOT_PREV_COND(var
, current
, head
, start
, cond
)
bool
GCQ_DEQUEUED_FIRST_COND(var
, head
, cond
)
bool
GCQ_DEQUEUED_LAST_COND(var
, head
, cond
)
bool
GCQ_DEQUEUED_NEXT_COND(var
, current
, head
, start
, cond
)
bool
GCQ_DEQUEUED_PREV_COND(var
, current
, head
, start
, cond
)
bool
GCQ_GOT_FIRST_COND_TYPED(tvar
, head
, type
, name
, cond
)
bool
GCQ_GOT_LAST_COND_TYPED(tvar
, head
, type
, name
, cond
)
bool
GCQ_GOT_NEXT_COND_TYPED(tvar
, current
, head
, start
, type
, name
, cond
)
bool
GCQ_GOT_PREV_COND_TYPED(tvar
, current
, head
, start
, type
, name
, cond
)
bool
GCQ_DEQUEUED_FIRST_COND_TYPED(tvar
, head
, type
, name
, cond
)
bool
GCQ_DEQUEUED_LAST_COND_TYPED(tvar
, head
, type
, name
, cond
)
bool
GCQ_DEQUEUED_NEXT_COND_TYPED(tvar
, current
, head
, start
, type
, name
, cond
)
bool
GCQ_DEQUEUED_PREV_COND_TYPED(tvar
, current
, head
, start
, type
, name
, cond
)
GCQ_FOREACH(var
, head
)
GCQ_FOREACH_REV(var
, head
)
GCQ_FOREACH_NVAR(var
, nvar
, head
)
GCQ_FOREACH_NVAR_REV(var
, nvar
, head
)
GCQ_FOREACH_RO(var
, nvar
, head
)
GCQ_FOREACH_RO_REV(var
, nvar
, head
)
GCQ_FOREACH_DEQUEUED(var
, nvar
, head
)
GCQ_FOREACH_DEQUEUED_REV(var
, nvar
, head
)
GCQ_FOREACH_TYPED(var
, head
, tvar
, type
, name
)
GCQ_FOREACH_REV_TYPED(var
, head
, tvar
, type
, name
)
GCQ_FOREACH_NVAR_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FOREACH_NVAR_REV_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FOREACH_RO_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FOREACH_RO_REV_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FOREACH_DEQUEUED_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FOREACH_DEQUEUED_REV_TYPED(var
, nvar
, head
, tvar
, type
, name
)
GCQ_FIND(var
, head
, cond
)
GCQ_FIND_REV(var
, head
, cond
)
GCQ_FIND_TYPED(var
, head
, tvar
, type
, name
, cond
)
GCQ_FIND_REV_TYPED(var
, head
, tvar
, type
, name
, cond
)
GCQ_ASSERT(cond
)
struct gcq {
struct gcq *q_next;
struct gcq *q_prev;
};
The structure must first be initialized such that the
q_next
and
q_prev
members point to the beginning of the
This can be done with
gcq_init()
and
gcq_init_head(
)
or with constant initializers
GCQ_INIT(
)
and
GCQ_INIT_HEAD(
).
A
should
never
be given
NULL
values.
The structure containing the
can be retrieved by pointer arithmetic in the
GCQ_ITEM()
macro.
List traversal normally requires knowledge of the list head to safely
retrieve list items.
Capitalized operation names are macros and should be assumed to cause multiple
evaluation of arguments.
TYPED
variants of macros set a typed pointer variable instead of or in addition to
arguments.
Additional type specific inlines and macros around some GCQ operations can be
useful.
A few assertions are provided when
DIAGNOSTIC
is defined in the kernel or
_DIAGNOSTIC
is defined in userland.
If
GCQ_USE_ASSERT
is defined prior to header inclusions
then
assert()
will be used for assertions and
NDEBUG
can be used to turn them off.
GCQ_ASSERT()
is a wrapper around the used assertion function.
None of the operations accept
NULL
arguements, however this is not tested by assertion.
The head is separately named for type checking but contains only a
a pointer to which can be retrieved via
gcq_hq().
The reverse operation is performed by
gcq_head(
),
turning the supplied
into
gcq_q(
)
returns its
argument and is used for type checking in
GCQ_ITEM(
).
There are no functions for retrieving the raw
q_prev
and
q_next
pointers as these are usually clearer when used directly (if at all).
gcq_remove()
returns the element removed and is always a valid operation after
initialization.
gcq_onlist(
)
returns
false
if the structure links to itself and
true
otherwise.
gcq_empty()
is the negation of this operation performed on a head.
gcq_linked(
)
tests if
prev->q_next == next && next->q_prev == prev
.
gcq_tie()
ties
src
after
dst
such that that if the old lists are DST, DST2 and SRC, SRC2, the new list is
DST, SRC, SRC2, DST2.
If
dst
and
src
are on the same list then any elements between but not including
dst
and
src
are cut from the list.
If
dst
==
src
then the result is the same as
gcq_remove().
gcq_tie(
)
is equivalent to
gcq_tie_after(
)
except that the latter must only be used with arguments on separate lists or
not on lists and asserts that
src != dst && dst->q_prev != src.
gcq_tie_before(
)
performs the same operation on
dst->q_prev
.
gcq_merge()
moves any elements on list
src
(but not
src
itself) to list
dst.
It is normally used with two heads via
gcq_merge_head(
)
or
gcq_merge_tail(
).
If
GCQ_UNCONDITIONAL_MERGE
is defined prior to header inclusion then the merge operations will always
perform a tie then remove
src
from the new list, which may reduce code size slightly.
gcq_clear()
initializes all elements currently linked with
q
and is normally used with a head as
gcq_remove_all(
).
gcq_insert_after()
and
gcq_insert_before(
)
are slightly optimized versions of
gcq_tie(
)
for the case where
off
is not on a list and include assertions to this effect, which are also useful
to detect missing initialization.
gcq_insert_head(
)
and
gcq_insert_tail(
)
are the same operations applied to a head.
GCQ_GOT_FIRST()
and
GCQ_GOT_LAST(
)
set
var
to a pointer to the first or last
in the list
or
NULL
if the list is empty and return
false
if empty and
true
otherwise.
The boolean return is to emphasise that it is not normally safe and useful to
directly pass the raw first/next/etc. pointer to another function.
The macros are written such that the
NULL
values will be optimized out if not otherwise used.
DEQUEUED
variants also remove the member from the list.
COND
variants take an additional condition that is evaluated when the macro would
otherwise return
true
.
If the condition is false
var
or
tvar
is set to
NULL
and no dequeue is performed.
GCQ_GOT_NEXT()
and variants take pointers to the current position, list head, and starting
point as arguments.
The list head will be skipped when it is reached unless it is equal to the
starting point; upon reaching the starting point
var
will be set to
NULL
and the macro will return
false
.
The next and prev macros also assert that
current
is on the list unless it is equal to
start.
These macros are the only provided method for iterating through the list from
an arbitrary point.
Traversal macros are only provided for list heads, however
gcq_head()
can be used to treat any item as a head.
Foreach variants contain an embedded
for
statement for iterating over a list.
Those containing
REV
use the
q_prev
pointer for traversal, others use
q_next.
The plain
GCQ_FOREACH()
uses a single variable.
NVAR
variants save the next pointer at the top of the loop so that the current
element can be removed without adjusting
var.
This is useful when
var
is passed to a function that might remove it but will not otherwise modify
the list.
When the head is reached both
var
and
nvar
elements are left pointing to the list head.
FOREACH
asserts that
var,
and
NVAR
asserts that
nvar
does not point to itself when starting the next loop.
This assertion takes place after the variable is tested against the head so
it is safe to remove all elements from the list.
RO
variants also set
nvar
but assert that the two variables are linked at the end of each iteration.
This is useful when calling a function that is not supposed to remove the
element passed.
DEQUEUED
varients are like
NVAR
but remove each element before the code block is executed.
TYPED
variants are equvalent to the untyped versions except that they take three
extra arguments: a typed pointer, the type name, and the member name of the
used in this list.
tvar
is set to
NULL
when the head is reached.
GCQ_FIND()
is a foreach loop that does nothing except break when the supplied condition
is true.
REV
and
TYPED
variants are available.