List nodes are represented by the ecu_dnode structure. They can store any user-defined type by being intrusive. Therefore a user’s node consists of data and an ecu_dnode member:
structuser_node{/* User data. */inta;intb;/* Store ecu_dnode in user node. */structecu_dnodenode;/* More user data. */intc;};
Linked list operations are performed on a user’s node by passing in the ecu_dnode member into this module:
Under the hood, these macros perform arithmetic on the supplied ecu_dnode pointer to convert it back into the user’s node type. The details of this operation are fully explained in ECU_CONTAINER_OF() and ECU_CONST_CONTAINER_OF().
A custom destroy callback can optionally be assigned to an ecu_dnode when it is constructed. This callback executes when the node is destroyed or when a list containing that node is destroyed. It performs additional cleanup specific to the the user’s data. In the example below, nodes are LEDs that turn off when destroyed:
structled{structecu_dnodenode;uint32_tport;uint32_tpin;};staticvoidturn_led_off(structecu_dnode*n,ecu_object_id_tid){/* Turn off LED when node is destroyed. */structled*me=ECU_DNODE_GET_ENTRY(n,structled,node);GPIOPinWriteLow(me->port,me->pin);}structledled1;ecu_dnode_ctor(&led1.node,&turn_led_off,ECU_OBJECT_ID_UNUSED);ecu_dnode_destroy(&led1.node);/* turn_led_off(&led1.node) runs. */
An optional ID can be assigned on construction to help identify different node types stored in the same list. For example:
/* User-defined IDs. */enumuser_node_ids{TYPE1=ECU_USER_OBJECT_ID_BEGIN,TYPE2,TYPE3};/* Different node types stored in same list. */structtype1{inta;structecu_dnodenode;};structtype2{structecu_dnodenode;intb;};structtype3{intc;structecu_dnodenode;intd;};structtype1node1;structtype2node2;structtype3node3;ecu_dnode_ctor(&node1.node,ECU_DNODE_DESTROY_UNUSED,TYPE1);ecu_dnode_ctor(&node2.node,ECU_DNODE_DESTROY_UNUSED,TYPE2);ecu_dnode_ctor(&node3.node,ECU_DNODE_DESTROY_UNUSED,TYPE3);/* Iterate over list containing these nodes. Use IDs to identify the node type.Assume list previously constructed and nodes added to list beforehand for conciseness. */ECU_DLIST_FOR_EACH(i,&iterator,&list){switch(ecu_dnode_id(i)){caseTYPE1:{structtype1*me=ECU_DNODE_GET_ENTRY(i,structtype1,node);me->a=5;break;}caseTYPE2:{structtype2*me=ECU_DNODE_GET_ENTRY(i,structtype2,node);me->b=10;break;}caseTYPE3:{structtype3*me=ECU_DNODE_GET_ENTRY(i,structtype3,node);me->c=15;me->d=20;break;}default:{break;}}}
Constructor. Initializes the ecu_dnode data structure for use. An optional destroy callback and node ID can also be assigned in the constructor. See Node Destroy Callback Section and Node ID Section for more details.
Warning
Must be called once on startup before the node is used. User is also responsible for allocating memory since ECU does not use dynamic memory allocation.
structecu_dnodenode1;/* User must allocate memory before constructor. */ecu_dnode_remove(&node1);/* ILLEGAL. Must construct before using. */ecu_dnode_ctor(&node1,ECU_DNODE_DESTROY_UNUSED,ECU_OBJECT_ID_UNUSED);ecu_dnode_remove(&node1);/* Ok. */
Destructor. Removes the node if it is in a list. Uninitializes the ecu_dnode structure then executes the user-defined destroy callback if one was supplied. See Node Destroy Callback Section for more details.
Warning
Memory is not freed when a node is destroyed since ECU library does not use dynamic memory allocation. Node can be freed within its destroy callback if it was allocated on the heap.
structecu_dnodenode;/* User must allocate memory before constructor. */ecu_dnode_ctor(&node,&user_destroy,ECU_OBJECT_ID_UNUSED);ecu_dnode_destroy(&node);/* ecu_dnode uninitialized and user_destroy(&node) ran. */
Returns the node previous to (left of) the queried node. NULL is returned if the queried node is one after HEAD or not in a list. Consider the following list:
Removes the node from a list. It can be reused and added to another list without reconstruction. If the supplied node is not in a list, this function does nothing.
Constructor. Initializes the ecu_dlist data structure for use.
Warning
Must be called once on startup before the list is used. User is also responsible for allocating memory since ECU does not use dynamic memory allocation.
structecu_dlistlist;/* User must allocate memory before constructor. */ecu_dlist_empty(&list);/* ILLEGAL. Must construct before using. */ecu_dlist_ctor(&list);ecu_dlist_empty(&list);/* Ok. */
Destructor. Destroys the list and all nodes within the list. All destroyed objects must be reconstructed in order to be used again. The user-supplied destroy callback for each node executes as they are destroyed. See Node Destroy Callback Section for more details.
Warning
Memory is not freed when a list is destroyed since ECU library does not use dynamic memory allocation. Destroyed nodes in the list can be freed within their destroy callbacks if they were allocated on the heap. The list must be freed after this destructor.
Inserts a node before the position specified by a condition function. Starting from HEAD, all nodes within the list are iterated over. Each node is passed as the position parameter to the condition function. The user returns true if the node should be inserted before this position or false to continue the iteration. This function exits as soon as the node is inserted. If the entire list is iterated over but no condition has returned true, the node is added to the back of the list.
Merge sorts all nodes in the list. The sorting condition is defined by a user-supplied function. This function must return true if the supplied left node (lhs) is less than the supplied right node (rhs). Otherwise false must be returned.
Iterates (for-loops) over list nodes, starting at the specified position. The specified starting node is included in the iteration, the iteration terminates after the TAIL is reached, and it is safe to remove the current node in the iteration.
Iterates (for-loops) over all list nodes starting at HEAD. HEAD is not included in the iteration, the iteration terminates after the TAIL is reached, and it is safe to remove the current node in the iteration.