LCOV - code coverage report
Current view: top level - libbpfilter/include/bpfilter - list.h (source / functions) Coverage Total Hit
Test: lcov.out Lines: 100.0 % 30 30
Test Date: 2025-10-28 16:24:15 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-2.0-only */
       2              : /*
       3              :  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
       4              :  */
       5              : 
       6              : #pragma once
       7              : 
       8              : #include <stdbool.h>
       9              : #include <stddef.h>
      10              : 
      11              : #include <bpfilter/helper.h>
      12              : #include <bpfilter/pack.h>
      13              : 
      14              : /**
      15              :  * @file list.h
      16              :  *
      17              :  * @todo `bf_list_add_XXX` functions should probably steal the pointer of the
      18              :  * data they receive, to be more consistent with other functions, and avoid
      19              :  * `TAKE_PTR()` after `bf_list_add_tail()`.
      20              :  */
      21              : 
      22              : /* This has to be defined here, otherwise struct bf_list_node definition is
      23              :  * self-referencing... */
      24              : typedef struct bf_list_node bf_list_node;
      25              : 
      26              : typedef void (*bf_list_ops_free)(void **data);
      27              : typedef int (*bf_list_ops_pack)(const void *data, bf_wpack_t *pack);
      28              : 
      29              : /**
      30              :  * @struct bf_list_ops
      31              :  * Operation to manipulate @ref bf_list data.
      32              :  *
      33              :  * @var bf_list_ops::free
      34              :  *  Free the data stored in a node. Should be able to handle NULL data.
      35              :  */
      36              : typedef struct
      37              : {
      38              :     /** Callback to free a node's data. If NULL, the data won't be freed. */
      39              :     bf_list_ops_free free;
      40              : 
      41              :     /** Callback to serialize the data from a node. If NULL, the data won't be
      42              :      * serialized. */
      43              :     bf_list_ops_pack pack;
      44              : } bf_list_ops;
      45              : 
      46              : /**
      47              :  * @struct bf_list_node
      48              :  * Node composing a @ref bf_list.
      49              :  *
      50              :  * @warning From the user's perspective, the inside of this structure should
      51              :  * not be directly accessed. Directly modifying any of the fields should be
      52              :  * considered undefined behavior.
      53              :  *
      54              :  * @var bf_list_node::data
      55              :  *      The node's data.
      56              :  * @var bf_list_node::prev
      57              :  *      Pointer to the previous node. Can be NULL if node is first.
      58              :  * @var bf_list_node::next
      59              :  *      Pointer to the next node. Can be NULL if node is last.
      60              :  */
      61              : struct bf_list_node
      62              : {
      63              :     void *data;
      64              :     bf_list_node *prev;
      65              :     bf_list_node *next;
      66              : };
      67              : 
      68              : /**
      69              :  * @struct bf_list
      70              :  * Base structure for the doubly-linked-list data structure.
      71              :  *
      72              :  * @warning From the user's perspective, the inside of this structure should
      73              :  * not be directly accessed. Directly modifying any of the fields should be
      74              :  * considered undefined behavior.
      75              :  *
      76              :  * @var bf_list::len
      77              :  *      Number of elements in the list.
      78              :  * @var bf_list::head
      79              :  *      First element of the list, NULL if the list is empty.
      80              :  * @var bf_list::tail
      81              :  *      Last element of the list, NULL if the list is empty.
      82              :  */
      83              : typedef struct bf_list
      84              : {
      85              :     size_t len;
      86              :     bf_list_node *head;
      87              :     bf_list_node *tail;
      88              :     bf_list_ops ops;
      89              : } bf_list;
      90              : 
      91              : #define _free_bf_list_ __attribute__((cleanup(bf_list_free)))
      92              : #define _clean_bf_list_ __attribute__((cleanup(bf_list_clean)))
      93              : 
      94              : /**
      95              :  * Iterate over a @ref bf_list.
      96              :  *
      97              :  * Use a temporary variable to store the next node (if any). Hence, a node
      98              :  * can be removed from the list during iteration.
      99              :  *
     100              :  * @param list Pointer to the list to iterate over. Must be non-NULL.
     101              :  * @param node Name of the variable containing the current node. This variable
     102              :  *        will be created automatically and the caller will be able to use it to
     103              :  *            access the node.
     104              :  */
     105              : #define bf_list_foreach(list, node)                                            \
     106              :     for (bf_list_node * (node) = (list)->head,                                 \
     107              :                         *__next = (list)->head ? (list)->head->next : NULL;    \
     108              :          (node); (node) = __next, __next = __next ? __next->next : NULL)
     109              : 
     110              : /**
     111              :  * Reverse iterate over a @ref bf_list.
     112              :  *
     113              :  * Use a temporary variable to store the next node (if any). Hence, a node
     114              :  * can be removed from the list during iteration.
     115              :  *
     116              :  * @param list Pointer to the list to iterate over. Must be non-NULL.
     117              :  * @param node Name of the variable containing the current node. This variable
     118              :  *            will be created automatically and the caller will be able to use it to
     119              :  *            access the node.
     120              :  */
     121              : #define bf_list_foreach_rev(list, node)                                        \
     122              :     for (bf_list_node * (node) = (list)->tail,                                 \
     123              :                         *__next = (list)->tail ? (list)->tail->prev : NULL;    \
     124              :          (node); (node) = __next, __next = __next ? __next->prev : NULL)
     125              : 
     126              : /**
     127              :  * Returns an initialised @ref bf_list_ops structure.
     128              :  *
     129              :  * @param free_cb Callback to free the data contained in a node. If NULL, a
     130              :  *        node's data won't be freed when the list is destroyed.
     131              :  * @param pack_cb Callback to serialize the data contained in a node. If NULL, a
     132              :  *        node's data won't be serialized when the list is serialized.
     133              :  * @return An initialised @ref bf_list_ops .
     134              :  */
     135              : #define bf_list_ops_default(free_cb, pack_cb)                                  \
     136              :     ((bf_list_ops) {.free = (bf_list_ops_free)(free_cb),                       \
     137              :                     .pack = (bf_list_ops_pack)(pack_cb)})
     138              : 
     139              : /**
     140              :  * Returns an initialised @ref bf_list .
     141              :  *
     142              :  * @param free_cb Callback to free the data contained in a node. If NULL, a
     143              :  *        node's data won't be freed when the list is destroyed.
     144              :  * @param pack_cb Callback to serialize the data contained in a node. If NULL, a
     145              :  *        node's data won't be serialized when the list is serialized.
     146              :  * @return An initialised @ref bf_list .
     147              :  */
     148              : #define bf_list_default(free_cb, pack_cb)                                      \
     149              :     ((bf_list) {.ops = bf_list_ops_default(free_cb, pack_cb)})
     150              : 
     151              : /**
     152              :  * Returns an initialized `bf_list` from an existing list.
     153              :  *
     154              :  * The returned list will be initialized with the callbacks defined in `list`.
     155              :  *
     156              :  * @param list Source list to initialize from.
     157              :  * @return An initialised `bf_list`.
     158              :  */
     159              : #define bf_list_default_from(list)                                             \
     160              :     ((bf_list) {.ops = bf_list_ops_default((list).ops.free, (list).ops.pack)})
     161              : 
     162              : /**
     163              :  * Move a list.
     164              :  *
     165              :  * Move a list from `list` and return it. Once moved, the original list can
     166              :  * either be reused, discarded, or `bf_list_clean` can be called on it safely.
     167              :  * The list it has been moved to will be overriden and `bf_list_clean` should be
     168              :  * called on it.
     169              :  *
     170              :  * @param list List to move.
     171              :  * @return The moved list.
     172              :  */
     173              : #define bf_list_move(list)                                                     \
     174              :     ({                                                                         \
     175              :         bf_list *__list = &(list);                                             \
     176              :         bf_list _list = *__list;                                               \
     177              :         *__list = bf_list_default(__list->ops.free, __list->ops.pack);         \
     178              :         _list;                                                                 \
     179              :     })
     180              : 
     181              : /**
     182              :  * Allocate and initialise a new list.
     183              :  *
     184              :  * @param list Pointer to the list to initialise. Must be non-NULL.
     185              :  * @param ops Operations to use to manipulate the list's data. If NULL, the
     186              :  *        list's ops are initialised to NULL: the node's data won't be free nor
     187              :  *        serialized.
     188              :  * @return 0 on success or negative errno code on failure.
     189              :  */
     190              : int bf_list_new(bf_list **list, const bf_list_ops *ops);
     191              : 
     192              : /**
     193              :  * Free a list.
     194              :  *
     195              :  * @param list Pointer to the list to free. Must be non-NULL.
     196              :  */
     197              : void bf_list_free(bf_list **list);
     198              : 
     199              : /**
     200              :  * Initialize an allocated list.
     201              :  *
     202              :  * @param list List to initialise. Must be non-NULL.
     203              :  * @param ops Operations to use to manipulate the list's data. If NULL, the
     204              :  *        list's ops are initialised to NULL: the node's data won't be free nor
     205              :  *        serialized.
     206              :  */
     207              : void bf_list_init(bf_list *list, const bf_list_ops *ops);
     208              : 
     209              : /**
     210              :  * Clean up a list.
     211              :  *
     212              :  * Every node in the list is freed. The node's data is freed using the function
     213              :  * provided during initialisation (through @ref bf_list_ops).
     214              :  *
     215              :  * @param list Pointer to the initialised list to clean. Must be non-NULL.
     216              :  */
     217              : void bf_list_clean(bf_list *list);
     218              : 
     219              : /**
     220              :  * @brief Serialize a list.
     221              :  *
     222              :  * Use `list.ops.pack` to serialize the list elements. If `list.ops.pack` is not
     223              :  * defined, the list will still be serialized, but empty.
     224              :  *
     225              :  * @param list List to serialize. Can't be NULL.
     226              :  * @param pack `bf_wpack_t` object to serialize the list into. Can't be NULL.
     227              :  * @return 0 on success, or a negative error value on failure.
     228              :  */
     229              : int bf_list_pack(const bf_list *list, bf_wpack_t *pack);
     230              : 
     231              : /**
     232              :  * Get the number of nodes in the list.
     233              :  *
     234              :  * @param list Initialised list. Must be non-NULL.
     235              :  * @return Number of nodes in the list.
     236              :  */
     237           35 : static inline size_t bf_list_size(const bf_list *list)
     238              : {
     239           41 :     bf_assert(list);
     240           26 :     return list->len;
     241              : }
     242              : 
     243              : /**
     244              :  * Check if a list is empty.
     245              :  *
     246              :  * @param list Initialised list. Must be non-NULL.
     247              :  * @return True if the list is empty, false otherwise.
     248              :  */
     249            1 : static inline bool bf_list_is_empty(const bf_list *list)
     250              : {
     251            1 :     bf_assert(list);
     252              : 
     253            1 :     return list->len == 0;
     254              : }
     255              : 
     256              : /**
     257              :  * Check if @p node is the head of @p list.
     258              :  *
     259              :  * @param list List. Must be non NULL.
     260              :  * @param node Node. Must be non NULL.
     261              :  * @return True if @p node is the head of @p list, false otherwise.
     262              :  */
     263              : static inline bool bf_list_is_head(const bf_list *list,
     264              :                                    const bf_list_node *node)
     265              : {
     266              :     bf_assert(list);
     267              :     bf_assert(node);
     268              : 
     269              :     return node == list->head;
     270              : }
     271              : 
     272              : /**
     273              :  * Check if @p node is the tail of @p list.
     274              :  *
     275              :  * @param list List. Must be non NULL.
     276              :  * @param node Node. Must be non NULL.
     277              :  * @return True if @p node is the tail of @p list, false otherwise.
     278              :  */
     279            8 : static inline bool bf_list_is_tail(const bf_list *list,
     280              :                                    const bf_list_node *node)
     281              : {
     282            8 :     bf_assert(list);
     283            7 :     bf_assert(node);
     284              : 
     285            6 :     return node == list->tail;
     286              : }
     287              : 
     288              : /**
     289              :  * @brief Push a new node at the end of the list and steal the data pointer.
     290              :  *
     291              :  * @param list List to push node to. Must be initialised and non-NULL.
     292              :  * @param data Data to be pushed. Must be non-NULL.
     293              :  * @return 0 on success or negative errno code on failure.
     294              :  */
     295              : int bf_list_push(bf_list *list, void **data);
     296              : 
     297              : /**
     298              :  * Add data at the beginning of the list.
     299              :  *
     300              :  * @param list List to append data to. Must be initialised and non-NULL.
     301              :  * @param data Data to append to the list. Can be NULL. @p list takes
     302              :  *            ownership of the data: it should not be freed.
     303              :  * @return 0 on success or negative errno code on failure.
     304              :  */
     305              : int bf_list_add_head(bf_list *list, void *data);
     306              : 
     307              : /**
     308              :  * Add data at the end of the list.
     309              :  *
     310              :  * @param list List to append data to. Must be initialised and non-NULL.
     311              :  * @param data Data to append to the list. Can be NULL. @p list takes
     312              :  *            ownership of the data: it should not be freed.
     313              :  * @return 0 on success or negative errno code on failure.
     314              :  */
     315              : int bf_list_add_tail(bf_list *list, void *data);
     316              : 
     317              : /**
     318              :  * Delete @p node from @p list.
     319              :  *
     320              :  * @p node is freed and shouldn't be used once the function returns. The node's
     321              :  * data will be freed using the function provided during initialisation (through
     322              :  * @ref bf_list_ops).
     323              :  *
     324              :  * @param list List to remove node from. Must be non-NULL.
     325              :  * @param node Node to remove from the list. Must be non-NULL.
     326              :  */
     327              : void bf_list_delete(bf_list *list, bf_list_node *node);
     328              : 
     329              : /**
     330              :  * Get the data of a node based on the node's index.
     331              :  *
     332              :  * @param list List to get the data from. Can't be NULL.
     333              :  * @param index Index of the node to get the data from. Index 0 would be the
     334              :  *              first node. If the node doesn't exist, NULL is returned.
     335              :  * @return Data containing in the node at index @c index , or NULL if the
     336              :  *         node doesn't exist.
     337              :  */
     338              : void *bf_list_get_at(const bf_list *list, size_t index);
     339              : 
     340              : /**
     341              :  * Returns the first element of the list.
     342              :  *
     343              :  * A @p bf_list_node object it returned. Use @ref bf_list_node_get_data to
     344              :  * get a pointer to the data.
     345              :  *
     346              :  * @param list Initialised list. Must be non-NULL.
     347              :  * @return Pointer to the first node of the list, or NULL if empty.
     348              :  */
     349            9 : static inline bf_list_node *bf_list_get_head(const bf_list *list)
     350              : {
     351           12 :     bf_assert(list);
     352            5 :     return list->head;
     353              : }
     354              : 
     355              : /**
     356              :  * Returns the last element of the list.
     357              :  *
     358              :  * A @p bf_list_node object it returned. Use @ref bf_list_node_get_data to
     359              :  * get a pointer to the data.
     360              :  *
     361              :  * @param list Initialised list. Must be non-NULL.
     362              :  * @return Pointer to the last node of the list, or NULL if empty.
     363              :  */
     364           15 : static inline bf_list_node *bf_list_get_tail(const bf_list *list)
     365              : {
     366           16 :     bf_assert(list);
     367            9 :     return list->tail;
     368              : }
     369              : 
     370              : /**
     371              :  * Get next node.
     372              :  *
     373              :  * @param node Current node. Must be non-NULL.
     374              :  * @return Pointer to the next node, or NULL if end of list.
     375              :  */
     376           11 : static inline bf_list_node *bf_list_node_next(const bf_list_node *node)
     377              : {
     378           11 :     bf_assert(node);
     379           10 :     return node->next;
     380              : }
     381              : 
     382              : /**
     383              :  * Get previous node.
     384              :  *
     385              :  * @param node Current node. Must be non-NULL.
     386              :  * @return Pointer to the previous node, or NULL if end of list.
     387              :  */
     388            3 : static inline bf_list_node *bf_list_node_prev(const bf_list_node *node)
     389              : {
     390            3 :     bf_assert(node);
     391            2 :     return node->prev;
     392              : }
     393              : 
     394              : /**
     395              :  * Get the node's data.
     396              :  *
     397              :  * Note that the pointer returned can be NULL, as nothing prevents NULL data
     398              :  * to be stored in the node.
     399              :  * The pointer returned is owned by the node and should not be freed.
     400              :  *
     401              :  * @param node Current node. Must be non-NULL.
     402              :  * @return Pointer to the data stored in the iterator.
     403              :  */
     404          254 : static inline void *bf_list_node_get_data(const bf_list_node *node)
     405              : {
     406          254 :     bf_assert(node);
     407          253 :     return node->data;
     408              : }
     409              : 
     410              : /**
     411              :  * Get the node's data and remove it from the node.
     412              :  *
     413              :  * Once the function returns, the node's data is set to NULL. The pointer
     414              :  * returned is then owned by the caller.
     415              :  *
     416              :  * @param node Current node. Must be non-NULL.
     417              :  * @return Pointer to the data stored in the node.
     418              :  */
     419            5 : static inline void *bf_list_node_take_data(bf_list_node *node)
     420              : {
     421              :     void *data;
     422              : 
     423            5 :     bf_assert(node);
     424              : 
     425            5 :     data = node->data;
     426            5 :     node->data = NULL;
     427              : 
     428            5 :     return data;
     429              : }
     430              : 
     431              : #define bf_list_emplace(list, fn, obj, ...)                                    \
     432              :     ({                                                                         \
     433              :         int __r = fn(&obj, ##__VA_ARGS__);                                     \
     434              :         if (!__r) {                                                            \
     435              :             __r = bf_list_add_tail(list, obj);                                 \
     436              :             if (!__r) {                                                        \
     437              :                 TAKE_PTR(obj);                                                 \
     438              :             } else {                                                           \
     439              :                 bf_err_r(__r, "failed to insert object into bf_list");         \
     440              :             }                                                                  \
     441              :         } else {                                                               \
     442              :             bf_err_r(__r, "failed to create object");                          \
     443              :         }                                                                      \
     444              :         __r;                                                                   \
     445              :     })
        

Generated by: LCOV version 2.0-1