Commit d7191d9d35 for openssl.org

commit d7191d9d35ce320fc1e81c95200078f261d411ce
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date:   Mon Jul 20 09:44:53 2026 +0200

    Add ossl_list_TYPE_join(head, tail) function

    The function appends list tail to list head. List tail becomes
    empty after the function returns.

    Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Reviewed-by: Norbert Pocs <norbertp@openssl.org>
    MergeDate: Thu Aug  6 11:57:39 2026
    (Merged from https://github.com/openssl/openssl/pull/32031)

diff --git a/doc/internal/man3/DEFINE_LIST_OF.pod b/doc/internal/man3/DEFINE_LIST_OF.pod
index d886defc43..6291a2d910 100644
--- a/doc/internal/man3/DEFINE_LIST_OF.pod
+++ b/doc/internal/man3/DEFINE_LIST_OF.pod
@@ -8,7 +8,7 @@ ossl_list_TYPE_is_empty, ossl_list_TYPE_num,
 ossl_list_TYPE_head, ossl_list_TYPE_tail,
 ossl_list_TYPE_next, ossl_list_TYPE_prev,
 ossl_list_TYPE_remove, ossl_list_TYPE_insert_head, ossl_list_TYPE_insert_tail,
-ossl_list_TYPE_insert_before, ossl_list_TYPE_after
+ossl_list_TYPE_insert_before, ossl_list_TYPE_after, ossl_list_TYPE_join
 - doubly linked list

 =head1 SYNOPSIS
@@ -38,6 +38,7 @@ ossl_list_TYPE_insert_before, ossl_list_TYPE_after
  void ossl_list_TYPE_insert_before(OSSL_LIST(name) *list, type *existing,
                                    type *elem);
  void ossl_list_TYPE_insert_after(OSSL_LIST(name) *list, type *existing, type *elem);
+ void ossl_list_TYPE_join(OSSL_LIST(name) *lh, OSSL_LIST(name) *lt);

 =head1 DESCRIPTION

@@ -90,6 +91,10 @@ B<ossl_list_I<TYPE>_insert_after>() inserts the element I<elem>,
 which must not be in the list, into the I<list> immediately after the
 I<existing> element.

+B<ossl_list_I<TYPE>_join<()> joins list B<lt> with list B<lh>.
+List B<lt> is appended to list B<lh>. The list B<lt> becomes empty,
+as all its members are part of B<lh> after the function returns.
+
 =head1 RETURN VALUES

 B<ossl_list_I<TYPE>_is_empty>() returns nonzero if the list is empty and zero
@@ -124,7 +129,9 @@ the specified element in the list.

 =head1 HISTORY

-The functions described here were all added in OpenSSL 3.2.
+ossl_list_TYPE_join() was added in OpenSSL 4.1, 4.0.2, 3.6.4, 3.5.8, and 3.4.7.
+
+The rest of the functions described here was added in OpenSSL 3.2.

 =head1 COPYRIGHT

diff --git a/include/internal/list.h b/include/internal/list.h
index 8bb0b741be..cd43471409 100644
--- a/include/internal/list.h
+++ b/include/internal/list.h
@@ -194,6 +194,35 @@
             list->omega = elem;                                                               \
         list->num_elems++;                                                                    \
     }                                                                                         \
+    static ossl_unused ossl_inline void                                                       \
+    ossl_list_##name##_join(OSSL_LIST(name) * lh, OSSL_LIST(name) * lt)                       \
+    {                                                                                         \
+        OSSL_LIST_DBG(type * _p); /* local variable '_p' when debug */                        \
+        if (lt == NULL || lh == NULL || lt->num_elems == 0 || lh == lt)                       \
+            return;                                                                           \
+        /*                                                                                    \
+         * let's be optimistic about size_t overflow here: it can not happen.                 \
+         */                                                                                   \
+        lh->num_elems += lt->num_elems;                                                       \
+        if (lh->omega == NULL) {                                                              \
+            assert(lh->alpha == NULL);                                                        \
+            lh->omega = lt->omega;                                                            \
+            lh->alpha = lt->alpha;                                                            \
+        } else {                                                                              \
+            if (lt->alpha != NULL)                                                            \
+                ((type *)lt->alpha)->ossl_list_##name.prev = lh->omega;                       \
+            ((type *)lh->omega)->ossl_list_##name.next = lt->alpha;                           \
+        }                                                                                     \
+        OSSL_LIST_DBG(for (_p = (type *)lt->alpha;                                            \
+                          assert(_p == NULL || _p->ossl_list_##name.list == lt), _p != NULL;  \
+                          _p = _p->ossl_list_##name.next)                                     \
+                          _p->ossl_list_##name.list                                           \
+            = lh);                                                                            \
+        lh->omega = lt->omega;                                                                \
+        lt->alpha = NULL;                                                                     \
+        lt->omega = NULL;                                                                     \
+        lt->num_elems = 0;                                                                    \
+    }                                                                                         \
     struct ossl_list_st_##name

 #define DEFINE_LIST_OF(name, type) \
diff --git a/test/list_test.c b/test/list_test.c
index a8a902554c..ffceeb4dc6 100644
--- a/test/list_test.c
+++ b/test/list_test.c
@@ -175,9 +175,55 @@ static int test_insert(void)
     return 1;
 }

+static int test_join(void)
+{
+    OSSL_LIST(int)
+    l_h, l_t;
+    INTL elem_h[20];
+    INTL elem_t[20];
+    int i;
+
+    ossl_list_int_init(&l_h);
+    ossl_list_int_init(&l_t);
+    ossl_list_int_join(&l_h, &l_t);
+    if (!TEST_size_t_eq(ossl_list_int_num(&l_t), 0))
+        return 0;
+
+    for (i = 0; i < (int)OSSL_NELEM(elem_h); i++) {
+        ossl_list_int_init_elem(&elem_h[i]);
+        elem_h[i].n = i;
+        ossl_list_int_insert_head(&l_h, &elem_h[i]);
+    }
+
+    for (i = 0; i < (int)OSSL_NELEM(elem_t); i++) {
+        ossl_list_int_init_elem(&elem_t[i]);
+        elem_t[i].n = i + 10;
+        ossl_list_int_insert_head(&l_t, &elem_t[i]);
+    }
+
+    ossl_list_int_join(NULL, NULL);
+
+    ossl_list_int_join(NULL, &l_t);
+    if (!TEST_size_t_eq(ossl_list_int_num(&l_t), OSSL_NELEM(elem_t)))
+        return 0;
+
+    ossl_list_int_join(&l_h, NULL);
+    if (!TEST_size_t_eq(ossl_list_int_num(&l_h), OSSL_NELEM(elem_h)))
+        return 0;
+
+    ossl_list_int_join(&l_h, &l_t);
+    if (!TEST_size_t_eq(ossl_list_int_num(&l_h), OSSL_NELEM(elem_h) + OSSL_NELEM(elem_t)))
+        return 0;
+
+    if (!TEST_true(ossl_list_int_is_empty(&l_t)))
+        return 0;
+
+    return 1;
+}
 int setup_tests(void)
 {
     ADD_TEST(test_fizzbuzz);
     ADD_TEST(test_insert);
+    ADD_TEST(test_join);
     return 1;
 }