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 : #include "bpfilter/list.h"
7 :
8 : #include <errno.h>
9 : #include <stdlib.h>
10 :
11 : #include "bpfilter/helper.h"
12 :
13 : /**
14 : * Create a new list node, with the given data.
15 : *
16 : * @param node New node pointer. Must be non-NULL.. If the function fails, this
17 : * parameter remains unchanged.
18 : * @param data Data to store in the new node. Can be NULL.
19 : * @return 0 on success or negative errno code on failure.
20 : */
21 306 : static int bf_list_node_new(bf_list_node **node, void *data)
22 : {
23 : bf_list_node *_node;
24 :
25 306 : bf_assert(node);
26 :
27 306 : _node = calloc(1, sizeof(*_node));
28 306 : if (!_node)
29 : return -ENOMEM;
30 :
31 306 : _node->data = data;
32 306 : *node = _node;
33 :
34 306 : return 0;
35 : }
36 :
37 : /**
38 : * Free a list node.
39 : *
40 : * @param node Node to free. Can't be NULL.
41 : * @param free_data Callback to use to free the data. If NULL, the data is not
42 : * freed.
43 : */
44 306 : static void bf_list_node_free(bf_list_node **node,
45 : void (*free_data)(void **data))
46 : {
47 306 : bf_assert(node);
48 :
49 306 : if (free_data)
50 295 : free_data(&(*node)->data);
51 : freep((void *)node);
52 306 : }
53 :
54 10 : int bf_list_new(bf_list **list, const bf_list_ops *ops)
55 : {
56 9 : _free_bf_list_ bf_list *_list = NULL;
57 :
58 10 : bf_assert(list);
59 :
60 9 : _list = calloc(1, sizeof(*_list));
61 9 : if (!_list)
62 : return -ENOMEM;
63 :
64 9 : bf_list_init(_list, ops);
65 :
66 9 : *list = TAKE_PTR(_list);
67 :
68 9 : return 0;
69 : }
70 :
71 21 : void bf_list_free(bf_list **list)
72 : {
73 21 : bf_assert(list);
74 :
75 20 : if (!*list)
76 : return;
77 :
78 9 : bf_list_clean(*list);
79 9 : free(*list);
80 9 : *list = NULL;
81 : }
82 :
83 50 : void bf_list_init(bf_list *list, const bf_list_ops *ops)
84 : {
85 50 : bf_assert(list);
86 :
87 49 : list->len = 0;
88 49 : list->head = NULL;
89 49 : list->tail = NULL;
90 :
91 49 : if (ops)
92 42 : list->ops = *ops;
93 : else
94 7 : list->ops = bf_list_ops_default(NULL, NULL);
95 49 : }
96 :
97 148 : void bf_list_clean(bf_list *list)
98 : {
99 148 : bf_assert(list);
100 :
101 886 : bf_list_foreach (list, node)
102 296 : bf_list_node_free(&node, list->ops.free);
103 :
104 147 : list->len = 0;
105 147 : list->head = NULL;
106 147 : list->tail = NULL;
107 147 : }
108 :
109 13 : int bf_list_pack(const bf_list *list, bf_wpack_t *pack)
110 : {
111 13 : bf_assert(list);
112 12 : bf_assert(pack);
113 :
114 11 : if (!list->ops.pack)
115 : return -ENOTSUP;
116 :
117 110 : bf_list_foreach (list, node) {
118 44 : if (!bf_list_node_get_data(node)) {
119 0 : bf_wpack_nil(pack);
120 0 : continue;
121 : }
122 :
123 44 : if (list->ops.pack == (bf_list_ops_pack)bf_list_pack) {
124 : // Handle nested lists
125 0 : bf_wpack_list(pack, bf_list_node_get_data(node));
126 : } else {
127 44 : bf_wpack_open_object(pack, NULL);
128 44 : list->ops.pack(bf_list_node_get_data(node), pack);
129 44 : bf_wpack_close_object(pack);
130 : }
131 : }
132 :
133 11 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
134 : }
135 :
136 49 : int bf_list_add_head(bf_list *list, void *data)
137 : {
138 49 : bf_list_node *node = NULL;
139 : int r;
140 :
141 49 : bf_assert(list);
142 :
143 48 : r = bf_list_node_new(&node, data);
144 48 : if (r < 0)
145 : return r;
146 :
147 48 : node->next = list->head;
148 48 : if (list->head)
149 38 : list->head->prev = node;
150 :
151 48 : list->head = node;
152 :
153 48 : if (!list->tail)
154 10 : list->tail = node;
155 :
156 48 : ++list->len;
157 :
158 48 : return 0;
159 : }
160 :
161 259 : int bf_list_add_tail(bf_list *list, void *data)
162 : {
163 259 : bf_list_node *node = NULL;
164 : int r;
165 :
166 259 : bf_assert(list);
167 :
168 258 : r = bf_list_node_new(&node, data);
169 258 : if (r < 0)
170 : return r;
171 :
172 258 : node->prev = list->tail;
173 258 : if (list->tail)
174 194 : list->tail->next = node;
175 :
176 258 : list->tail = node;
177 :
178 258 : if (!list->head)
179 64 : list->head = node;
180 :
181 258 : ++list->len;
182 :
183 258 : return 0;
184 : }
185 :
186 10 : void bf_list_delete(bf_list *list, bf_list_node *node)
187 : {
188 10 : bf_assert(list);
189 10 : bf_assert(node);
190 :
191 10 : if (list->head == node)
192 10 : list->head = node->next;
193 10 : if (list->tail == node)
194 1 : list->tail = node->prev;
195 :
196 10 : if (node->prev)
197 0 : node->prev->next = node->next;
198 10 : if (node->next)
199 9 : node->next->prev = node->prev;
200 :
201 10 : bf_list_node_free(&node, list->ops.free);
202 :
203 10 : --list->len;
204 10 : }
205 :
206 11 : void *bf_list_get_at(const bf_list *list, size_t index)
207 : {
208 11 : bf_assert(list);
209 :
210 77 : bf_list_foreach (list, node) {
211 38 : if (index == 0)
212 9 : return node->data;
213 29 : --index;
214 : }
215 :
216 : return NULL;
217 : }
|