From: Pauli Date: Fri, 2 Sep 2022 04:44:02 +0000 (+1000) Subject: list: add a doubly linked list type. X-Git-Tag: openssl-3.2.0-alpha1~2127 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f5eac259a03c68c96c77f9b998b1b9c16a8439e7;p=thirdparty%2Fopenssl.git list: add a doubly linked list type. 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 Reviewed-by: Richard Levitte Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/19115) --- diff --git a/doc/internal/man3/DEFINE_LIST_OF.pod b/doc/internal/man3/DEFINE_LIST_OF.pod new file mode 100644 index 00000000000..ade676b3260 --- /dev/null +++ b/doc/internal/man3/DEFINE_LIST_OF.pod @@ -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> 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>. This is a structure which should be treated as opaque. + +DEFINE_LIST_OF() creates a set of functions for a list of B> +elements with the name B>. The type is represented +by B(B>) and each function name begins with +B_>. The list's linkages are stored in the +B(B>, B>) field. + +B_init>() initialises the memory pointed to by I +to zero which creates an empty list. + +B_num>() returns the number of elements in I. + +B_head>() returns the first element in the I +or NULL if there are no elements. + +B_tail>() returns the last element in the I +or NULL if there are no elements. + +B_remove>() removes the specified element I from +the I. It is illegal to remove an element that isn't in the list. + +B_insert_head>() inserts the element I, which +must not be in the list, into the first position in the I. + +B_insert_tail>() inserts the element I, which +must not be in the list, into the last position in the I. + +B_insert_before>() inserts the element I, +which must not be in the list, into the I immediately before the +I element. + +B_insert_after>() inserts the element I, +which must not be in the list, into the I immediately after the +I element. + +=head1 RETURN VALUES + +B_num>() returns the number of elements in the +list. + +B_head>(), B_tail>(), +B_next>() and B_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. + +=cut diff --git a/include/internal/list.h b/include/internal/list.h new file mode 100644 index 00000000000..5686a88537e --- /dev/null +++ b/include/internal/list.h @@ -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 + +/* 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 diff --git a/test/build.info b/test/build.info index 44dff9d475d..0fac691cab3 100644 --- a/test/build.info +++ b/test/build.info @@ -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 index 00000000000..5f89e5c6adb --- /dev/null +++ b/test/list_test.c @@ -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 +#include + +#include +#include +#include + +#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 index 00000000000..40e5a439c57 --- /dev/null +++ b/test/recipes/02-test_list.t @@ -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"])));