Skip to content

Commit 9416324

Browse files
committed
sys: dlist: add API to split lists
Add a constant-time operation that splits a list into a portion before a node and the portion starting at the node, moving the portion before the node to the end of a different list and leaving the original list starting at the node. Add a constant-time list join operation to append an existing list to a different list. This supports use cases where a dlist is split at some point (e.g. timers that have reached their deadline), so that the extracted elements can be processed without affecting content added to the dlist during processing. Signed-off-by: Peter A. Bigot <[email protected]>
1 parent fb0d742 commit 9416324

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

include/misc/dlist.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,68 @@ static inline sys_dnode_t *sys_dlist_get(sys_dlist_t *list)
552552
return node;
553553
}
554554

555+
/**
556+
* @brief place the contents of one list at the end of another list.
557+
*
558+
* The @p to and @p from lists must be distinct. On completion @p from
559+
* will be empty, all of its elements having been appended in original
560+
* order to @p to.
561+
*
562+
* @param to a list, possibly non-empty, to which from will be appended
563+
* @param from the list providing the elements to append
564+
*
565+
* @return N/A
566+
*/
567+
static inline void sys_dlist_join(sys_dlist_t *to,
568+
sys_dlist_t *from)
569+
{
570+
if (!sys_dlist_is_empty(from)) {
571+
from->head->prev = to->tail;
572+
to->tail->next = from->head;
573+
574+
from->tail->next = to;
575+
to->tail = from->tail;
576+
577+
sys_dlist_init(from);
578+
}
579+
}
580+
581+
/**
582+
* @brief split a list at a node
583+
*
584+
* list will be updated to start at node. Any nodes before node will
585+
* be appended to prefix.
586+
*
587+
* This and other sys_dlist_*() functions are not thread safe.
588+
*
589+
* @param prefix a list to which items in @p list before @p node
590+
* will be appended
591+
* @param list a non-empty list
592+
* @param node a node within @p list
593+
*
594+
* @return N/A
595+
*/
596+
static inline void sys_dlist_split(sys_dlist_t *prefix,
597+
sys_dlist_t *list,
598+
sys_dnode_t *node)
599+
{
600+
sys_dnode_t *old_pfx_tail = prefix->tail;
601+
sys_dnode_t *new_pfx_tail = node->prev;
602+
603+
if (sys_dlist_peek_head(list) == node) {
604+
return;
605+
}
606+
607+
list->head->prev = old_pfx_tail;
608+
old_pfx_tail->next = list->head;
609+
610+
prefix->tail = new_pfx_tail;
611+
new_pfx_tail->next = prefix;
612+
613+
list->head = node;
614+
node->prev = list;
615+
}
616+
555617
#ifdef __cplusplus
556618
}
557619
#endif

tests/kernel/common/src/dlist.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <misc/dlist.h>
99

1010
static sys_dlist_t test_list;
11+
static sys_dlist_t test_list2;
1112

1213
struct container_node {
1314
sys_dnode_t node;
@@ -281,6 +282,74 @@ void test_dlist(void)
281282
zassert_true((verify_emptyness(&test_list)),
282283
"test_list should be empty");
283284

285+
286+
/* Catenate an empty list to a non-empty list */
287+
sys_dlist_append(&test_list, &test_node_1.node);
288+
sys_dlist_init(&test_list2);
289+
sys_dlist_join(&test_list, &test_list2);
290+
zassert_true(sys_dlist_is_empty(&test_list2),
291+
"list2 not empty");
292+
zassert_true((verify_tail_head(&test_list, &test_node_1.node,
293+
&test_node_1.node, true)),
294+
"test_list head/tail are wrong");
295+
296+
/* Catenate a non-empty list to an empty list moves elements. */
297+
sys_dlist_join(&test_list2, &test_list);
298+
zassert_true(sys_dlist_is_empty(&test_list),
299+
"list not empty");
300+
zassert_true((verify_tail_head(&test_list2, &test_node_1.node,
301+
&test_node_1.node, true)),
302+
"test_list2 head/tail are wrong");
303+
304+
/* Catenate a non-empty list to a non-empty list moves elements. */
305+
sys_dlist_append(&test_list, &test_node_2.node);
306+
sys_dlist_append(&test_list, &test_node_3.node);
307+
zassert_true((verify_tail_head(&test_list, &test_node_2.node,
308+
&test_node_3.node, false)),
309+
"test_list head/tail are wrong");
310+
sys_dlist_join(&test_list2, &test_list);
311+
zassert_true(sys_dlist_is_empty(&test_list),
312+
"list not empty");
313+
zassert_true((verify_tail_head(&test_list2, &test_node_1.node,
314+
&test_node_3.node, false)),
315+
"test_list2 head/tail are wrong");
316+
zassert_equal(test_node_1.node.next, &test_node_2.node,
317+
"node2 not after node1");
318+
zassert_equal(test_node_2.node.prev, &test_node_1.node,
319+
"node1 not before node2");
320+
321+
/* Split list at head does nothing */
322+
sys_dlist_split(&test_list, &test_list2, &test_node_1.node);
323+
zassert_true(sys_dlist_is_empty(&test_list),
324+
"list not empty");
325+
326+
/* Split list after head moves */
327+
sys_dlist_split(&test_list, &test_list2, &test_node_2.node);
328+
zassert_true((verify_tail_head(&test_list, &test_node_1.node,
329+
&test_node_1.node, true)),
330+
"test_list head/tail are wrong");
331+
zassert_true((verify_tail_head(&test_list2, &test_node_2.node,
332+
&test_node_3.node, false)),
333+
"test_list2 head/tail are wrong");
334+
335+
/* Split list after head moves */
336+
sys_dlist_split(&test_list, &test_list2, &test_node_3.node);
337+
zassert_true((verify_tail_head(&test_list, &test_node_1.node,
338+
&test_node_2.node, false)),
339+
"test_list head/tail are wrong");
340+
zassert_true((verify_tail_head(&test_list2, &test_node_3.node,
341+
&test_node_3.node, true)),
342+
"test_list2 head/tail are wrong");
343+
344+
sys_dlist_remove(&test_node_1.node);
345+
sys_dlist_remove(&test_node_2.node);
346+
zassert_true(sys_dlist_is_empty(&test_list),
347+
"list not empty");
348+
349+
sys_dlist_remove(&test_node_3.node);
350+
zassert_true(sys_dlist_is_empty(&test_list2),
351+
"list2 not empty");
352+
284353
/* test iterator from a node */
285354
struct data_node {
286355
sys_dnode_t node;

0 commit comments

Comments
 (0)