--- /dev/null
+=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
--- /dev/null
+/*
+ * 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
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
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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#! /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"])));