]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
list: add a doubly linked list type.
authorPauli <pauli@openssl.org>
Fri, 2 Sep 2022 04:44:02 +0000 (14:44 +1000)
committerPauli <pauli@openssl.org>
Mon, 5 Sep 2022 06:24:53 +0000 (16:24 +1000)
These list can be embedded into structures and structures can be members of
multiple lists.  Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL;
         p = ossl_list_new_items_next(p))
        /* do something */

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19115)

doc/internal/man3/DEFINE_LIST_OF.pod [new file with mode: 0644]
include/internal/list.h [new file with mode: 0644]
test/build.info
test/list_test.c [new file with mode: 0644]
test/recipes/02-test_list.t [new file with mode: 0644]

diff --git a/doc/internal/man3/DEFINE_LIST_OF.pod b/doc/internal/man3/DEFINE_LIST_OF.pod
new file mode 100644 (file)
index 0000000..ade676b
--- /dev/null
@@ -0,0 +1,126 @@
+=pod
+
+=head1 NAME
+
+DEFINE_LIST_OF, OSSL_LIST_MEMBER, OSSL_LIST,
+ossl_list_TYPE_init, 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
+- doubly linked list
+
+=head1 SYNOPSIS
+
+=for openssl generic
+
+ #include "internal/list.h"
+
+ OSSL_LIST(name);
+ OSSL_LIST_MEMBER(NAME, TYPE);
+ DEFINE_LIST_OF(NAME, TYPE);
+
+ void ossl_list_TYPE_init(OSSL_LIST(name) *list);
+
+ size_t ossl_list_TYPE_num(const OSSL_LIST(name) *list);
+ type *ossl_list_TYPE_head(const OSSL_LIST(name) *list);
+ type *ossl_list_TYPE_tail(const OSSL_LIST(name) *list);
+
+ type *ossl_list_TYPE_next(const type *elem);
+ type *ossl_list_TYPE_prev(const type *elem);
+
+ void ossl_list_TYPE_remove(OSSL_LIST(name) *list, type *elem);
+ void ossl_list_TYPE_insert_head(OSSL_LIST(name) *list, type *elem);
+ void ossl_list_TYPE_insert_tail(OSSL_LIST(name) *list, type *elem);
+ 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);
+
+=head1 DESCRIPTION
+
+Create type safe linked list.  These macros define typesafe inline
+functions that implement the various list operations. In the description
+here, B<I<TYPE>> is used as a placeholder for any datatype.  Lists are intended to
+be incorporated into other structures and rather than being a standalone data
+structure.
+
+The OSSL_LIST() macro returns the name for a list of the specified
+B<I<TYPE>>.  This is a structure which should be treated as opaque.
+
+DEFINE_LIST_OF() creates a set of functions for a list of B<I<TYPE>>
+elements with the name B<I<NAME>>.  The type is represented
+by B<OSSL_LIST>(B<I<NAME>>) and each function name begins with
+B<ossl_list_I<NAME>_>.  The list's linkages are stored in the
+B<OSSL_LIST_MEMBER>(B<I<NAME>>, B<I<TYPE>>) field.
+
+B<ossl_list_I<NAME>_init>() initialises the memory pointed to by I<list>
+to zero which creates an empty list.
+
+B<ossl_list_I<NAME>_num>() returns the number of elements in I<list>.
+
+B<ossl_list_I<NAME>_head>() returns the first element in the I<list>
+or NULL if there are no elements.
+
+B<ossl_list_I<NAME>_tail>() returns the last element in the I<list>
+or NULL if there are no elements.
+
+B<ossl_list_I<NAME>_remove>() removes the specified element I<elem> from
+the I<list>.  It is illegal to remove an element that isn't in the list.
+
+B<ossl_list_I<NAME>_insert_head>() inserts the element I<elem>, which
+must not be in the list, into the first position in the I<list>.
+
+B<ossl_list_I<NAME>_insert_tail>() inserts the element I<elem>, which
+must not be in the list, into the last position in the I<list>.
+
+B<ossl_list_I<NAME>_insert_before>() inserts the element I<elem>,
+which must not be in the list, into the I<list> immediately before the
+I<existing> element.
+
+B<ossl_list_I<NAME>_insert_after>() inserts the element I<elem>,
+which must not be in the list, into the I<list> immediately after the
+I<existing> element.
+
+=head1 RETURN VALUES
+
+B<ossl_list_I<NAME>_num>() returns the number of elements in the
+list.
+
+B<ossl_list_I<NAME>_head>(), B<ossl_list_I<NAME>_tail>(),
+B<ossl_list_I<NAME>_next>() and B<ossl_list_I<NAME>_prev>() return
+the specified element in the list.
+
+=head1 EXAMPLES
+
+ typedef struct item_st ITEM;
+
+ struct item_st {
+     ...
+     OSSL_LIST_MEMBER(new_items, ITEM);
+     ...
+ };
+
+ DEFINE_LIST_OF(new_items, ITEM);
+
+ OSSL_LIST(new_items) new;
+
+ ITEM *p;
+
+ for (p = ossl_list_new_items_head(&st->new); p != NULL;
+      p = ossl_list_new_items_next(p))
+     /* do something */
+
+=head1 HISTORY
+
+The functions described here were all added in OpenSSL 3.1.
+
+=head1 COPYRIGHT
+
+Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/internal/list.h b/include/internal/list.h
new file mode 100644 (file)
index 0000000..5686a88
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_INTERNAL_LIST_H
+# define OSSL_INTERNAL_LIST_H
+# pragma once
+
+# include <string.h>
+
+/* Define a list structure */
+# define OSSL_LIST(name) OSSL_LIST_ ## name
+
+/* Define fields to include an element of a list */
+# define OSSL_LIST_MEMBER(name, type)                                       \
+    struct {                                                                \
+        type *next, *prev;                                                  \
+    } ossl_list_ ## name
+
+# define DEFINE_LIST_OF(name, type)                                         \
+    typedef struct ossl_list_st_ ## name OSSL_LIST(name);                   \
+    struct ossl_list_st_ ## name {                                          \
+        type *head, *tail;                                                  \
+        size_t num_elems;                                                   \
+    };                                                                      \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_init(OSSL_LIST(name) *list)                          \
+    {                                                                       \
+        memset(list, 0, sizeof(*list));                                     \
+    }                                                                       \
+    static ossl_unused ossl_inline size_t                                   \
+    ossl_list_##name##_num(const OSSL_LIST(name) *list)                     \
+    {                                                                       \
+        return list->num_elems;                                             \
+    }                                                                       \
+    static ossl_unused ossl_inline type *                                   \
+    ossl_list_##name##_head(const OSSL_LIST(name) *list)                    \
+    {                                                                       \
+        return list->head;                                                  \
+    }                                                                       \
+    static ossl_unused ossl_inline type *                                   \
+    ossl_list_##name##_tail(const OSSL_LIST(name) *list)                    \
+    {                                                                       \
+        return list->tail;                                                  \
+    }                                                                       \
+    static ossl_unused ossl_inline type *                                   \
+    ossl_list_##name##_next(const type *elem)                               \
+    {                                                                       \
+        return elem->ossl_list_ ## name.next;                               \
+    }                                                                       \
+    static ossl_unused ossl_inline type *                                   \
+    ossl_list_##name##_prev(const type *elem)                               \
+    {                                                                       \
+        return elem->ossl_list_ ## name.prev;                               \
+    }                                                                       \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_remove(OSSL_LIST(name) *list, type *elem)            \
+    {                                                                       \
+        if (list->head == elem)                                             \
+            list->head = elem->ossl_list_ ## name.next;                     \
+        if (list->tail == elem)                                             \
+            list->tail = elem->ossl_list_ ## name.prev;                     \
+        if (elem->ossl_list_ ## name.prev != NULL)                          \
+            elem->ossl_list_ ## name.prev->ossl_list_ ## name.next =        \
+                    elem->ossl_list_ ## name.next;                          \
+        if (elem->ossl_list_ ## name.next != NULL)                          \
+            elem->ossl_list_ ## name.next->ossl_list_ ## name.prev =        \
+                    elem->ossl_list_ ## name.prev;                          \
+        list->num_elems--;                                                  \
+        memset(&elem->ossl_list_ ## name, 0,                                \
+               sizeof(elem->ossl_list_ ## name));                           \
+    }                                                                       \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_insert_head(OSSL_LIST(name) *list, type *elem)       \
+    {                                                                       \
+        if (list->head != NULL)                                             \
+            list->head->ossl_list_ ## name.prev = elem;                     \
+        elem->ossl_list_ ## name.next = list->head;                         \
+        elem->ossl_list_ ## name.prev = NULL;                               \
+        list->head = elem;                                                  \
+        if (list->tail == NULL)                                             \
+            list->tail = elem;                                              \
+        list->num_elems++;                                                  \
+    }                                                                       \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_insert_tail(OSSL_LIST(name) *list, type *elem)       \
+    {                                                                       \
+        if (list->tail != NULL)                                             \
+            list->tail->ossl_list_ ## name.next = elem;                     \
+        elem->ossl_list_ ## name.prev = list->tail;                         \
+        elem->ossl_list_ ## name.next = NULL;                               \
+        list->tail = elem;                                                  \
+        if (list->head == NULL)                                             \
+            list->head = elem;                                              \
+        list->num_elems++;                                                  \
+    }                                                                       \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_insert_before(OSSL_LIST(name) *list, type *e,        \
+                                     type *elem)                            \
+    {                                                                       \
+        elem->ossl_list_ ## name.next = e;                                  \
+        elem->ossl_list_ ## name.prev = e->ossl_list_ ## name.prev;         \
+        if (e->ossl_list_ ## name.prev != NULL)                             \
+            e->ossl_list_ ## name.prev->ossl_list_ ## name.next = elem;     \
+        e->ossl_list_ ## name.prev = elem;                                  \
+        if (list->head == e)                                                \
+            list->head = elem;                                              \
+        list->num_elems++;                                                  \
+    }                                                                       \
+    static ossl_unused ossl_inline void                                     \
+    ossl_list_##name##_insert_after(OSSL_LIST(name) *list, type *e,         \
+                                     type *elem)                            \
+    {                                                                       \
+        elem->ossl_list_ ## name.prev = e;                                  \
+        elem->ossl_list_ ## name.next = e->ossl_list_ ## name.next;         \
+        if (e->ossl_list_ ## name.next != NULL)                             \
+            e->ossl_list_ ## name.next->ossl_list_ ## name.prev = elem;     \
+        e->ossl_list_ ## name.next = elem;                                  \
+        if (list->tail == e)                                                \
+            list->tail = elem;                                              \
+        list->num_elems++;                                                  \
+    }                                                                       \
+    struct ossl_list_st_ ## name
+
+#endif
index 44dff9d475d963de7a35ca925b943719bc1bf1b1..0fac691cab3954d5e3a5963389a537248884b4e7 100644 (file)
@@ -63,7 +63,7 @@ IF[{- !$disabled{tests} -}]
           keymgmt_internal_test hexstr_test provider_status_test defltfips_test \
           bio_readbuffer_test user_property_test pkcs7_test upcallstest \
           provfetchtest prov_config_test rand_test ca_internals_test \
-          bio_tfo_test membio_test
+          bio_tfo_test membio_test list_test
 
   IF[{- !$disabled{'deprecated-3.0'} -}]
     PROGRAMS{noinst}=enginetest
@@ -737,6 +737,10 @@ IF[{- !$disabled{tests} -}]
     INCLUDE[dhtest]=../include ../apps/include
     DEPEND[dhtest]=../libcrypto.a libtestutil.a
 
+    SOURCE[list_test]=list_test.c
+    INCLUDE[list_test]=../include ../apps/include
+    DEPEND[list_test]=libtestutil.a
+
     SOURCE[hmactest]=hmactest.c
     INCLUDE[hmactest]=../include ../apps/include
     DEPEND[hmactest]=../libcrypto.a libtestutil.a
diff --git a/test/list_test.c b/test/list_test.c
new file mode 100644 (file)
index 0000000..5f89e5c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/opensslconf.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+
+#include "internal/list.h"
+#include "internal/nelem.h"
+#include "testutil.h"
+
+typedef struct testl_st TESTL;
+struct testl_st {
+    int n;
+    OSSL_LIST_MEMBER(fizz, TESTL);
+    OSSL_LIST_MEMBER(buzz, TESTL);
+};
+
+DEFINE_LIST_OF(fizz, TESTL);
+DEFINE_LIST_OF(buzz, TESTL);
+
+static int test_fizzbuzz(void)
+{
+    OSSL_LIST(fizz) a;
+    OSSL_LIST(buzz) b;
+    TESTL elem[20];
+    const int nelem = OSSL_NELEM(elem);
+    int i, na = 0, nb = 0;
+
+    ossl_list_fizz_init(&a);
+    ossl_list_buzz_init(&b);
+
+    for (i = 1; i < nelem; i++) {
+        elem[i].n = i;
+        if (i % 3 == 0) {
+            ossl_list_fizz_insert_tail(&a, elem + i);
+            na++;
+        }
+        if (i % 5 == 0) {
+            ossl_list_buzz_insert_head(&b, elem + i);
+            nb++;
+        }
+    }
+
+    if (!TEST_size_t_eq(ossl_list_fizz_num(&a), na)
+            || !TEST_size_t_eq(ossl_list_buzz_num(&b), nb)
+            || !TEST_int_eq(ossl_list_fizz_head(&a)->n, 3)
+            || !TEST_int_eq(ossl_list_fizz_tail(&a)->n, na * 3)
+            || !TEST_int_eq(ossl_list_buzz_head(&b)->n, nb * 5)
+            || !TEST_int_eq(ossl_list_buzz_tail(&b)->n, 5))
+        return 0;
+    ossl_list_fizz_remove(&a, ossl_list_fizz_head(&a));
+    ossl_list_buzz_remove(&b, ossl_list_buzz_tail(&b));
+    if (!TEST_size_t_eq(ossl_list_fizz_num(&a), --na)
+            || !TEST_size_t_eq(ossl_list_buzz_num(&b), --nb)
+            || !TEST_int_eq(ossl_list_fizz_head(&a)->n, 6)
+            || !TEST_int_eq(ossl_list_buzz_tail(&b)->n, 10)
+            || !TEST_int_eq(ossl_list_fizz_next(ossl_list_fizz_head(&a))->n, 9)
+            || !TEST_int_eq(ossl_list_fizz_prev(ossl_list_fizz_tail(&a))->n, 15))
+        return 0;
+    return 1;
+}
+
+typedef struct int_st INTL;
+struct int_st {
+    int n;
+    OSSL_LIST_MEMBER(int, INTL);
+};
+
+DEFINE_LIST_OF(int, INTL);
+
+static int test_insert(void)
+{
+    INTL *c, *d;
+    OSSL_LIST(int) l;
+    INTL elem[20];
+    size_t i;
+    int n = 1;
+
+    ossl_list_int_init(&l);
+    for (i = 0; i < OSSL_NELEM(elem); i++)
+            elem[i].n = i;
+
+    /* Check various insert options - head, tail, middle */
+    ossl_list_int_insert_head(&l, elem + 3);                /* 3 */
+    ossl_list_int_insert_tail(&l, elem + 6);                /* 3 6 */
+    ossl_list_int_insert_before(&l, elem + 6, elem + 5);    /* 3 5 6 */
+    ossl_list_int_insert_before(&l, elem + 3, elem + 1);    /* 1 3 5 6 */
+    ossl_list_int_insert_after(&l, elem + 1, elem + 2);     /* 1 2 3 5 6 */
+    ossl_list_int_insert_after(&l, elem + 6, elem + 7);     /* 1 2 3 5 6 7 */
+    ossl_list_int_insert_after(&l, elem + 3, elem + 4);     /* 1 2 3 4 5 6 7 */
+    if (!TEST_size_t_eq(ossl_list_int_num(&l), 7))
+        return 0;
+    c = ossl_list_int_head(&l);
+    d = ossl_list_int_tail(&l);
+    while (c != NULL && d != NULL) {
+        if (!TEST_int_eq(c->n, n) || !TEST_int_eq(d->n, 8 - n))
+            return 0;
+        c = ossl_list_int_next(c);
+        d = ossl_list_int_prev(d);
+        n++;
+    }
+    if (!TEST_ptr_null(c) || !TEST_ptr_null(d))
+        return 0;
+
+    /* Check removing head, tail and middle */
+    ossl_list_int_remove(&l, elem + 1);                     /* 2 3 4 5 6 7 */
+    ossl_list_int_remove(&l, elem + 6);                     /* 2 3 4 5 7 */
+    ossl_list_int_remove(&l, elem + 7);                     /* 2 3 4 5 */
+    n = 2;
+    c = ossl_list_int_head(&l);
+    d = ossl_list_int_tail(&l);
+    while (c != NULL && d != NULL) {
+        if (!TEST_int_eq(c->n, n) || !TEST_int_eq(d->n, 7 - n))
+            return 0;
+        c = ossl_list_int_next(c);
+        d = ossl_list_int_prev(d);
+        n++;
+    }
+    if (!TEST_ptr_null(c) || !TEST_ptr_null(d))
+        return 0;
+
+    /* Check removing the head of a two element list works */
+    ossl_list_int_remove(&l, elem + 2);                     /* 3 4 5 */
+    ossl_list_int_remove(&l, elem + 4);                     /* 3 5 */
+    ossl_list_int_remove(&l, elem + 3);                     /* 5 */
+    if (!TEST_int_eq(ossl_list_int_head(&l)->n, 5)
+            || !TEST_int_eq(ossl_list_int_tail(&l)->n, 5))
+        return 0;
+
+    /* Check removing the tail of a two element list works */
+    ossl_list_int_insert_head(&l, elem);                    /* 0 5 */
+    ossl_list_int_remove(&l, elem + 5);                     /* 0 */
+    if (!TEST_int_eq(ossl_list_int_head(&l)->n, 0)
+            || !TEST_int_eq(ossl_list_int_tail(&l)->n, 0))
+        return 0;
+
+    /* Check removing the only element works */
+    ossl_list_int_remove(&l, elem);
+    if (!TEST_ptr_null(ossl_list_int_head(&l))
+            || !TEST_ptr_null(ossl_list_int_tail(&l)))
+        return 0;
+    return 1;
+}
+
+int setup_tests(void)
+{
+    ADD_TEST(test_fizzbuzz);
+    ADD_TEST(test_insert);
+    return 1;
+}
diff --git a/test/recipes/02-test_list.t b/test/recipes/02-test_list.t
new file mode 100644 (file)
index 0000000..40e5a43
--- /dev/null
@@ -0,0 +1,16 @@
+#! /usr/bin/env perl
+# Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use OpenSSL::Test;
+use OpenSSL::Test::Utils;
+
+setup("test_list");
+
+plan tests => 1;
+
+ok(run(test(["list_test"])));