diff --git a/lib/list.h b/lib/list.h index fc0419da..8a5c165e 100644 --- a/lib/list.h +++ b/lib/list.h @@ -116,4 +116,37 @@ __END_EXTERN_C list_head_init((struct list_head *)(HEAD)); \ CHECK_TYPE(&(HEAD)->first, (HEAD)->tailnextp) +/* + * LIST_SPLICE_TAIL/LIST_SPLICE_HEAD + * + * move all items on the SRC list to the TAIL/HEAD of the DST list. + * O(1) regardless of the number of items to move. + * this leaves the SRC list in an inconsistent state. it's up to + * the user to do LIST_HEAD_INIT(SRC) if necessary. + */ + +#define LIST_SPLICE_TAIL(DST, SRC, NAME) \ + do { \ + if (!LIST_EMPTY(SRC)) { \ + (SRC)->first->NAME.prevnextp = (DST)->tailnextp; \ + *(DST)->tailnextp = (SRC)->first; \ + (DST)->tailnextp = (SRC)->tailnextp; \ + } \ + } while (0) + +#define LIST_SPLICE_HEAD(DST, SRC, NAME) \ + do { \ + if (!LIST_EMPTY(SRC)) { \ + if (LIST_EMPTY(DST)) { \ + (DST)->tailnextp = (SRC)->tailnextp; \ + } else { \ + (DST)->first->NAME.prevnextp = \ + (SRC)->tailnextp; \ + *(SRC)->tailnextp = (DST)->first; \ + } \ + (DST)->first = (SRC)->first; \ + (DST)->first->NAME.prevnextp = &(DST)->first; \ + } \ + } while (0) + #endif /* !defined(_TOYWASM_LIST_H) */ diff --git a/test/test.c b/test/test.c index 6b4ffadf..f78bc0e4 100644 --- a/test/test.c +++ b/test/test.c @@ -767,6 +767,104 @@ test_list(void **state) assert_null(LIST_LAST(&h, struct item, entry)); } +void +test_list2(void **state) +{ + struct item { + void *dummy1; + LIST_ENTRY(struct item) entry; + int dummy2; + }; + + LIST_HEAD(struct item) h0; + LIST_HEAD(struct item) h1; + LIST_HEAD(struct item) h2; + LIST_HEAD(struct item) hempty; + + struct item i1; + struct item i2; + struct item i3; + struct item i4; + struct item i5; + struct item i6; + + /* test LIST_SPLICE_TAIL */ + + LIST_HEAD_INIT(&h0); + + LIST_HEAD_INIT(&h1); + LIST_INSERT_TAIL(&h1, &i1, entry); + LIST_INSERT_TAIL(&h1, &i2, entry); + LIST_INSERT_TAIL(&h1, &i3, entry); + + LIST_HEAD_INIT(&h2); + LIST_INSERT_HEAD(&h2, &i6, entry); + LIST_INSERT_HEAD(&h2, &i5, entry); + LIST_INSERT_HEAD(&h2, &i4, entry); + + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_TAIL(&h0, &hempty, entry); + LIST_SPLICE_TAIL(&h0, &h1, entry); + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_TAIL(&h0, &hempty, entry); + LIST_SPLICE_TAIL(&h0, &h2, entry); + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_TAIL(&h0, &hempty, entry); + + assert_ptr_equal(LIST_FIRST(&h0), &i1); + assert_ptr_equal(LIST_NEXT(&i1, entry), &i2); + assert_ptr_equal(LIST_NEXT(&i2, entry), &i3); + assert_ptr_equal(LIST_NEXT(&i3, entry), &i4); + assert_ptr_equal(LIST_NEXT(&i4, entry), &i5); + assert_ptr_equal(LIST_NEXT(&i5, entry), &i6); + assert_null(LIST_NEXT(&i6, entry)); + assert_ptr_equal(LIST_LAST(&h0, struct item, entry), &i6); + assert_ptr_equal(LIST_PREV(&i6, &h0, struct item, entry), &i5); + assert_ptr_equal(LIST_PREV(&i5, &h0, struct item, entry), &i4); + assert_ptr_equal(LIST_PREV(&i4, &h0, struct item, entry), &i3); + assert_ptr_equal(LIST_PREV(&i3, &h0, struct item, entry), &i2); + assert_ptr_equal(LIST_PREV(&i2, &h0, struct item, entry), &i1); + assert_null(LIST_PREV(&i1, &h0, struct item, entry)); + + /* test LIST_SPLICE_HEAD */ + + LIST_HEAD_INIT(&h0); + + LIST_HEAD_INIT(&h1); + LIST_INSERT_TAIL(&h1, &i1, entry); + LIST_INSERT_TAIL(&h1, &i2, entry); + LIST_INSERT_TAIL(&h1, &i3, entry); + + LIST_HEAD_INIT(&h2); + LIST_INSERT_HEAD(&h2, &i6, entry); + LIST_INSERT_HEAD(&h2, &i5, entry); + LIST_INSERT_HEAD(&h2, &i4, entry); + + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_HEAD(&h0, &hempty, entry); + LIST_SPLICE_HEAD(&h0, &h1, entry); + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_HEAD(&h0, &hempty, entry); + LIST_SPLICE_HEAD(&h0, &h2, entry); + LIST_HEAD_INIT(&hempty); + LIST_SPLICE_HEAD(&h0, &hempty, entry); + + assert_ptr_equal(LIST_FIRST(&h0), &i4); + assert_ptr_equal(LIST_NEXT(&i4, entry), &i5); + assert_ptr_equal(LIST_NEXT(&i5, entry), &i6); + assert_ptr_equal(LIST_NEXT(&i6, entry), &i1); + assert_ptr_equal(LIST_NEXT(&i1, entry), &i2); + assert_ptr_equal(LIST_NEXT(&i2, entry), &i3); + assert_null(LIST_NEXT(&i3, entry)); + assert_ptr_equal(LIST_LAST(&h0, struct item, entry), &i3); + assert_ptr_equal(LIST_PREV(&i3, &h0, struct item, entry), &i2); + assert_ptr_equal(LIST_PREV(&i2, &h0, struct item, entry), &i1); + assert_ptr_equal(LIST_PREV(&i1, &h0, struct item, entry), &i6); + assert_ptr_equal(LIST_PREV(&i6, &h0, struct item, entry), &i5); + assert_ptr_equal(LIST_PREV(&i5, &h0, struct item, entry), &i4); + assert_null(LIST_PREV(&i4, &h0, struct item, entry)); +} + void test_xstrnstr(void **state) { @@ -824,6 +922,7 @@ main(int argc, char **argv) cmocka_unit_test(test_timeutil), cmocka_unit_test(test_timeutil_int64), cmocka_unit_test(test_list), + cmocka_unit_test(test_list2), cmocka_unit_test(test_xstrnstr), }; return cmocka_run_group_tests(tests, NULL, NULL);