doc: strcutures: use doxygen to reference functions

Use :c:func:`..` possible to reference and link doxygen documentation.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Anas Nashif 2021-04-14 10:31:45 -04:00
commit d31d6ce0be
5 changed files with 74 additions and 73 deletions

View file

@ -11,28 +11,28 @@ the head, tail or any internal node). To do this, the list stores two
pointers per node, and thus has somewhat higher runtime code and
memory space needs.
A ``sys_dlist_t`` struct may be instantiated by the user in any
accessible memory. It must be initialized with ``sys_dlist_init()``
or ``SYS_DLIST_STATIC_INIT()`` before use. The ``sys_dnode_t`` struct
A :c:struct:`sys_dlist_t` struct may be instantiated by the user in any
accessible memory. It must be initialized with :c:func:`sys_dlist_init`
or :c:macro:`SYS_DLIST_STATIC_INIT` before use. The :c:struct:`sys_dnode_t` struct
is expected to be provided by the user for any nodes addded to the
list (typically embedded within the struct to be tracked, as described
above). It must be initialized in zeroed/bss memory or with
``sys_dnode_init()`` before use.
:c:func:`sys_dnode_init` before use.
Primitive operations may retrieve the head/tail of a list and the
next/prev pointers of a node with ``sys_dlist_peek_head()``,
``sys_dlist_peek_tail()``, ``sys_dlist_peek_next()`` and
``sys_dlist_peek_prev()``. These can all return NULL where
next/prev pointers of a node with :c:func:`sys_dlist_peek_head`,
:c:func:`sys_dlist_peek_tail`, :c:func:`sys_dlist_peek_next` and
:c:func:`sys_dlist_peek_prev`. These can all return NULL where
appropriate (i.e. for empty lists, or nodes at the endpoints of the
list).
A dlist can be modified in constant time by removing a node with
``sys_dlist_remove()``, by adding a node to the head or tail of a list
with ``sys_dlist_prepend()`` and ``sys_dlist_append()``, or by
inserting a node before an existing node with ``sys_dlist_insert()``.
:c:func:`sys_dlist_remove`, by adding a node to the head or tail of a list
with :c:func:`sys_dlist_prepend` and :c:func:`sys_dlist_append`, or by
inserting a node before an existing node with :c:func:`sys_dlist_insert`.
As for slist, each node in a dlist can be processed in a natural code
block style using ``SYS_DLIST_FOR_EACH_NODE()``. This macro also
block style using :c:macro:`SYS_DLIST_FOR_EACH_NODE`. This macro also
exists in a "FROM_NODE" form which allows for iterating from a known
starting point, a "SAFE" variant that allows for removing the node
being inspected within the code block, a "CONTAINER" style that
@ -40,18 +40,18 @@ provides the pointer to a containing struct instead of the raw node,
and a "CONTAINER_SAFE" variant that provides both properties.
Convenience utilities provided by dlist include
``sys_dlist_insert_at()``, which inserts a node that linearly searches
:c:func:`sys_dlist_insert_at`, which inserts a node that linearly searches
through a list to find the right insertion point, which is provided by
the user as a C callback function pointer, and
``sys_dlist_is_linked()``, which will affirmatively return whether or
:c:func:`sys_dnode_is_linked`, which will affirmatively return whether or
not a node is currently linked into a dlist or not (via an
implementation that has zero overhead vs. the normal list processing).
Double-linked List Internals
----------------------------
Internally, the dlist implementation is minimal: the ``sys_dlist_t``
struct contains "head" and "tail" pointer fields, the ``sys_dnode_t``
Internally, the dlist implementation is minimal: the :c:struct:`sys_dlist_t`
struct contains "head" and "tail" pointer fields, the :c:struct:`sys_dnode_t`
contains "prev" and "next" pointers, and no other data is stored. But
in practice the two structs are internally identical, and the list
struct is inserted as a node into the list itself. This allows for a

View file

@ -10,15 +10,15 @@ which has runtimes on search and removal operations bounded at
O(log2(N)) for a tree of size N. This is implemented using a
conventional red/black tree as described by multiple academic sources.
The ``struct rbtree`` tracking struct for a rbtree may be initialized
The :c:struct:`rbtree` tracking struct for a rbtree may be initialized
anywhere in user accessible memory. It should contain only zero bits
before first use. No specific initialization API is needed or
required.
Unlike a list, where position is explicit, the ordering of nodes
within an rbtree must be provided as a predicate function by the user.
A function of type ``rb_lessthan_t()`` should be assigned to the
``lessthan_fn`` field of the ``struct rbtree`` before any tree
A function of type :c:func:`rb_lessthan_t` should be assigned to the
``lessthan_fn`` field of the :c:struct`rbtree` struct before any tree
operations are attempted. This function should, as its name suggests,
return a boolean True value if the first node argument is "less than"
the second in the ordering desired by the tree. Note that "equal" is
@ -26,40 +26,40 @@ not allowed, nodes within a tree must have a single fixed order for
the algorithm to work correctly.
As with the slist and dlist containers, nodes within an rbtree are
represented as a ``struct rbnode`` structure which exists in
represented as a :c:struct:`rbnode` structure which exists in
user-managed memory, typically embedded within the the data structure
being tracked in the tree. Unlike the list code, the data within an
rbnode is entirely opaque. It is not possible for the user to extract
the binary tree topology and "manually" traverse the tree as it is for
a list.
Nodes can be inserted into a tree with ``rb_insert()`` and removed
with ``rb_remove()``. Access to the "first" and "last" nodes within a
Nodes can be inserted into a tree with :c:func:`rb_insert` and removed
with :c:func:`rb_remove`. Access to the "first" and "last" nodes within a
tree (in the sense of the order defined by the comparison function) is
provided by ``rb_get_min()`` and ``rb_get_max()``. There is also a
predicate, ``rb_contains()``, which returns a boolean True if the
provided by :c:func:`rb_get_min` and :c:func:`rb_get_max`. There is also a
predicate, :c:func:`rb_contains`, which returns a boolean True if the
provided node pointer exists as an element within the tree. As
described above, all of these routines are guaranteed to have at most
log time complexity in the size of the tree.
There are two mechanisms provided for enumerating all elements in an
rbtree. The first, ``rb_walk()``, is a simple callback implementation
rbtree. The first, :c:func:`rb_walk`, is a simple callback implementation
where the caller specifies a C function pointer and an untyped
argument to be passed to it, and the tree code calls that function for
each node in order. This has the advantage of a very simple
implementation, at the cost of a somewhat more cumbersome API for the
user (not unlike ISO C's ``bsearch()`` routine). It is a recursive
user (not unlike ISO C's :c:func:`bsearch` routine). It is a recursive
implementation, however, and is thus not always available in
environments that forbid the use of unbounded stack techniques like
recursion.
There is also a ``RB_FOR_EACH()`` iterator provided, which, like the
There is also a :c:macro:`RB_FOR_EACH` iterator provided, which, like the
similar APIs for the lists, works to iterate over a list in a more
natural way, using a nested code block instead of a callback. It is
also nonrecursive, though it requires log-sized space on the stack by
default (however, this can be configured to use a fixed/maximally size
buffer instead where needed to avoid the dynamic allocation). As with
the lists, this is also available in a ``RB_FOR_EACH_CONTAINER()``
the lists, this is also available in a :c:macro:`RB_FOR_EACH_CONTAINER`
variant which enumerates using a pointer to a container field and not
the raw node pointer.
@ -94,12 +94,12 @@ modification.
These rotations are conceptually implemented on top of a primitive
that "swaps" the position of one node with another in the list.
Typical implementations effect this by simply swapping the nodes
internal "data" pointers, but because the Zephyr ``struct rbnode`` is
internal "data" pointers, but because the Zephyr :c:struct:`rbnode` is
intrusive, that cannot work. Zephyr must include somewhat more
elaborate code to handle the edge cases (for example, one swapped node
can be the root, or the two may already be parent/child).
The ``struct rbnode`` struct for a Zephyr rbtree contains only two
The :c:struct:`rbnode` struct for a Zephyr rbtree contains only two
pointers, representing the "left", and "right" children of a node
within the binary tree. Traversal of a tree for rebalancing following
modification, however, routinely requires the ability to iterate

View file

@ -52,62 +52,62 @@ data buffer to empty.
A ``struct ring_buf`` may be placed anywhere in user-accessible
memory, and must be initialized with ``ring_buf_init()`` before use.
memory, and must be initialized with :c:func:`ring_buf_init` before use.
This must be provided a region of user-controlled memory for use as
the buffer itself. Note carefully that the units of the size of the
buffer passed change (either bytes or words) depending on how the ring
buffer will be used later. Macros for combining these steps in a
single static declaration exist for convenience.
``RING_BUF_DECLARE()`` will declare and statically initialize a ring
:c:macro:`RING_BUF_DECLARE` will declare and statically initialize a ring
buffer with a specified byte count, where
``RING_BUF_ITEM_DECLARE_SIZE()`` will declare and statically
:c:macro:`RING_BUF_ITEM_DECLARE_SIZE` will declare and statically
initialize a buffer with a given count of 32 bit words.
``RING_BUF_ITEM_DECLARE_POW2()`` can be used to initialize an
:c:macro:`RING_BUF_ITEM_DECLARE_POW2` can be used to initialize an
items-mode buffer with a memory region guaranteed to be a power of
two, which enables various optimizations internal to the
implementation. No power-of-two initialization is available for
bytes-mode ring buffers.
"Bytes" data may be copied into the ring buffer using
``ring_buf_put()``, passing a data pointer and byte count. These
:c:func:`ring_buf_put`, passing a data pointer and byte count. These
bytes will be copied into the buffer in order, as many as will fit in
the allocated buffer. The total number of bytes copied (which may be
fewer than provided) will be returned. Likewise ``ring_buf_get()``
fewer than provided) will be returned. Likewise :c:func:`ring_buf_get`
will copy bytes out of the ring buffer in the order that they were
written, into a user-provided buffer, returning the number of bytes
that were transferred.
To avoid multiply-copied-data situations, a "claim" API exists for
byte mode. ``ring_buf_put_claim()`` takes a byte size value from the
byte mode. :c:func:`ring_buf_put_claim` takes a byte size value from the
user and returns a pointer to memory internal to the ring buffer that
can be used to receive those bytes, along with a size of the
contiguous internal region (which may be smaller than requested). The
user can then copy data into that region at a later time without
assembling all the bytes in a single region first. When complete,
``ring_buf_put_finish()`` can be used to signal the buffer that the
:c:func:`ring_buf_put_finish` can be used to signal the buffer that the
transfer is complete, passing the number of bytes actually
transferred. At this point a new transfer can be initiated.
Similarly, ``ring_buf_get_claim()`` returns a pointer to internal ring
Similarly, :c:func:`ring_buf_get_claim` returns a pointer to internal ring
buffer data from which the user can read without making a verbatim
copy, and ``ring_buf_get_finish()`` signals the buffer with how many
copy, and :c:func:`ring_buf_get_finish` signals the buffer with how many
bytes have been consumed and allows for a new transfer to begin.
"Items" mode works similarly to bytes mode, except that all transfers
are in units of 32 bit words and all memory is assumed to be aligned
on 32 bit boundaries. The write and read operations are
``ring_buf_item_put()`` and ``ring_buf_item_get()``, and work
:c:func:`ring_buf_item_put` and :c:func:`ring_buf_item_get`, and work
otherwise identically to the bytes mode APIs. There no "claim" API
provided for items mode. One important difference is that unlike
``ring_buf_put()``, ``ring_buf_item_put()`` will not do a partial
:c:func:`ring_buf_put`, :c:func:`ring_buf_item_put` will not do a partial
transfer; it will return an error in the case where the provided data
does not fit in its entirety.
The user can manage the capacity of a ring buffer without modifying it
using the ``ring_buf_space_get()`` call (which returns a value of
using the :c:func:`ring_buf_space_get` call (which returns a value of
either bytes or items depending on how the ring buffer has been used),
or by testing the ``ring_buf_is_empty()`` predicate.
or by testing the :c:func:`ring_buf_is_empty` predicate.
Finally, a ``ring_buf_reset()`` call exists to immediately empty a
Finally, a :c:func:`ring_buf_reset` call exists to immediately empty a
ring buffer, discarding the tracking of any bytes or items already
written to the buffer. It does not modify the memory contents of the
buffer itself, however.

View file

@ -3,7 +3,7 @@
Single-linked List
==================
Zephyr provides a ``sys_slist_t`` type for storing simple
Zephyr provides a :c:struct:`sys_slist_t` type for storing simple
singly-linked list data (i.e. data where each list element stores a
pointer to the next element, but not the previous one). This supports
constant-time access to the first (head) and last (tail) elements of
@ -12,53 +12,53 @@ constant time removal of the head. Removal of subsequent nodes
requires access to the "previous" pointer and thus can only be
performed in linear time by searching the list.
The ``sys_slist_t`` struct may be instantiated by the user in any
The :c:struct:`sys_slist_t` struct may be instantiated by the user in any
accessible memory. It should be initialized with either
``sys_slist_init()`` or by static assignment from SYS_SLIST_STATIC_INIT
:c:func:`sys_slist_init` or by static assignment from SYS_SLIST_STATIC_INIT
before use. Its interior fields are opaque and should not be accessed
by user code.
The end nodes of a list may be retrieved with
``sys_slist_peek_head()`` and ``sys_slist_peek_tail()``, which will
:c:func:`sys_slist_peek_head` and :c:func:`sys_slist_peek_tail`, which will
return NULL if the list is empty, otherwise a pointer to a
``sys_snode_t`` struct.
:c:struct:`sys_snode_t` struct.
The ``sys_snode_t`` struct represents the data to be inserted. In
The :c:struct:`sys_snode_t` struct represents the data to be inserted. In
general, it is expected to be allocated/controlled by the user,
usually embedded within a struct which is to be added to the list.
The container struct pointer may be retrieved from a list node using
``SYS_SLIST_CONTAINER()``, passing it the struct name of the
:c:macro:`SYS_SLIST_CONTAINER`, passing it the struct name of the
containing struct and the field name of the node. Internally, the
``sys_snode_t`` struct contains only a next pointer, which may be
accessed with ``sys_slist_peek_next()``.
:c:struct:`sys_snode_t` struct contains only a next pointer, which may be
accessed with :c:func:`sys_slist_peek_next`.
Lists may be modified by adding a single node at the head or tail with
``sys_slist_prepend()`` and ``sys_slist_append()``. They may also
have a node added to an interior point with ``sys_slist_insert()``,
:c:func:`sys_slist_prepend` and :c:func:`sys_slist_append`. They may also
have a node added to an interior point with :c:func:`sys_slist_insert`,
which inserts a new node after an existing one. Similarly
``sys_slist_remove()`` will remove a node given a pointer to its
:c:func:`sys_slist_remove` will remove a node given a pointer to its
predecessor. These operations are all constant time.
Convenience routines exist for more complicated modifications to a
list. ``sys_slist_merge_slist()`` will append an entire list to an
existing one. ``sys_slist_append_list()`` will append a bounded
list. :c:func:`sys_slist_merge_slist` will append an entire list to an
existing one. :c:func:`sys_slist_append_list` will append a bounded
subset of an existing list in constant time. And
``sys_slist_find_and_remove()`` will search a list (in linear time)
:c:func:`sys_slist_find_and_remove` will search a list (in linear time)
for a given node and remove it if present.
Finally the slist implementation provides a set of "for each" macros
that allows for iterating over a list in a natural way without needing
to manually traverse the next pointers. ``SYS_SLIST_FOR_EACH_NODE()``
to manually traverse the next pointers. :c:macro:`SYS_SLIST_FOR_EACH_NODE`
will enumerate every node in a list given a local variable to store
the node pointer. ``SYS_SLIST_FOR_EACH_NODE_SAFE()`` behaves
the node pointer. :c:macro:`SYS_SLIST_FOR_EACH_NODE_SAFE` behaves
similarly, but has a more complicated implementation that requires an
extra scratch variable for storage and allows the user to delete the
iterated node during the iteration. Each of those macros also exists
in a "container" variant (``SYS_SLIST_FOR_EACH_CONTAINER()`` and
``SYS_SLIST_FOR_EACH_CONTAINER_SAFE()``) which assigns a local
in a "container" variant (:c:macro:`SYS_SLIST_FOR_EACH_CONTAINER` and
:c:macro:`SYS_SLIST_FOR_EACH_CONTAINER_SAFE`) which assigns a local
variable of a type that matches the user's container struct and not
the node struct, performing the required offsets internally. And
``SYS_SLIST_ITERATE_FROM_NODE()`` exists to allow for enumerating a
:c:macro:`SYS_SLIST_ITERATE_FROM_NODE` exists to allow for enumerating a
node and all its successors only, without inspecting the earlier part
of the list.
@ -66,8 +66,8 @@ Single-linked List Internals
----------------------------
The slist code is designed to be minimal and conventional.
Internally, a ``sys_slist_t`` struct is nothing more than a pair of
"head" and "tail" pointer fields. And a ``sys_snode_t`` stores only a
Internally, a :c:struct:`sys_slist_t` struct is nothing more than a pair of
"head" and "tail" pointer fields. And a :c:struct:`sys_snode_t` stores only a
single "next" pointer.
.. figure:: slist.png
@ -101,14 +101,14 @@ Only one such variant, sflist, exists in Zephyr at the moment.
Flagged List
------------
The ``sys_sflist_t`` is implemented using the described genlist
The :c:struct:`sys_sflist_t` is implemented using the described genlist
template API. With the exception of symbol naming ("sflist" instead
of "slist") and the additional API described next, it operates in all
ways identically to the slist API.
It adds the ability to associate exactly two bits of user defined
"flags" with each list node. These can be accessed and modified with
``sys_sflist_flags_get()`` and ``sys_sflist_flags_get()``.
:c:func:`sys_sfnode_flags_get` and :c:func:`sys_sfnode_flags_get`.
Internally, the flags are stored unioned with the bottom bits of the
next pointer and incur no SRAM storage overhead when compared with the
simpler slist code.

View file

@ -28,6 +28,12 @@
extern "C" {
#endif
/**
* @defgroup doubly-linked-list_apis Doubly-linked list
* @ingroup datastructure_apis
* @{
*/
struct _dnode {
union {
struct _dnode *head; /* ptr to head of list (sys_dlist_t) */
@ -42,11 +48,6 @@ struct _dnode {
typedef struct _dnode sys_dlist_t;
typedef struct _dnode sys_dnode_t;
/**
* @defgroup doubly-linked-list_apis Doubly-linked list
* @ingroup datastructure_apis
* @{
*/
/**
* @brief Provide the primitive to iterate on a list