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 309 : static int bf_list_node_new(bf_list_node **node, void *data)
22 : {
23 : bf_list_node *_node;
24 :
25 309 : bf_assert(node);
26 :
27 309 : _node = calloc(1, sizeof(*_node));
28 309 : if (!_node)
29 : return -ENOMEM;
30 :
31 309 : _node->data = data;
32 309 : *node = _node;
33 :
34 309 : 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 309 : static void bf_list_node_free(bf_list_node **node,
45 : void (*free_data)(void **data))
46 : {
47 309 : bf_assert(node);
48 :
49 309 : if (free_data)
50 298 : free_data(&(*node)->data);
51 : freep((void *)node);
52 309 : }
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 51 : void bf_list_init(bf_list *list, const bf_list_ops *ops)
84 : {
85 51 : bf_assert(list);
86 :
87 50 : list->len = 0;
88 50 : list->head = NULL;
89 50 : list->tail = NULL;
90 :
91 50 : if (ops)
92 43 : list->ops = *ops;
93 : else
94 7 : list->ops = bf_list_ops_default(NULL, NULL);
95 50 : }
96 :
97 149 : void bf_list_clean(bf_list *list)
98 : {
99 149 : bf_assert(list);
100 :
101 894 : bf_list_foreach (list, node)
102 299 : bf_list_node_free(&node, list->ops.free);
103 :
104 148 : list->len = 0;
105 148 : list->head = NULL;
106 148 : list->tail = NULL;
107 148 : }
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 5 : int bf_list_push(bf_list *list, void **data)
137 : {
138 : int r;
139 :
140 5 : bf_assert(data);
141 :
142 3 : r = bf_list_add_tail(list, *data);
143 2 : if (r < 0)
144 : return r;
145 :
146 2 : TAKE_PTR(*data);
147 :
148 2 : return 0;
149 : }
150 :
151 49 : int bf_list_add_head(bf_list *list, void *data)
152 : {
153 49 : bf_list_node *node = NULL;
154 : int r;
155 :
156 49 : bf_assert(list);
157 :
158 48 : r = bf_list_node_new(&node, data);
159 48 : if (r < 0)
160 : return r;
161 :
162 48 : node->next = list->head;
163 48 : if (list->head)
164 38 : list->head->prev = node;
165 :
166 48 : list->head = node;
167 :
168 48 : if (!list->tail)
169 10 : list->tail = node;
170 :
171 48 : ++list->len;
172 :
173 48 : return 0;
174 : }
175 :
176 263 : int bf_list_add_tail(bf_list *list, void *data)
177 : {
178 263 : bf_list_node *node = NULL;
179 : int r;
180 :
181 263 : bf_assert(list);
182 :
183 261 : r = bf_list_node_new(&node, data);
184 261 : if (r < 0)
185 : return r;
186 :
187 261 : node->prev = list->tail;
188 261 : if (list->tail)
189 196 : list->tail->next = node;
190 :
191 261 : list->tail = node;
192 :
193 261 : if (!list->head)
194 65 : list->head = node;
195 :
196 261 : ++list->len;
197 :
198 261 : return 0;
199 : }
200 :
201 10 : void bf_list_delete(bf_list *list, bf_list_node *node)
202 : {
203 10 : bf_assert(list);
204 10 : bf_assert(node);
205 :
206 10 : if (list->head == node)
207 10 : list->head = node->next;
208 10 : if (list->tail == node)
209 1 : list->tail = node->prev;
210 :
211 10 : if (node->prev)
212 0 : node->prev->next = node->next;
213 10 : if (node->next)
214 9 : node->next->prev = node->prev;
215 :
216 10 : bf_list_node_free(&node, list->ops.free);
217 :
218 10 : --list->len;
219 10 : }
220 :
221 11 : void *bf_list_get_at(const bf_list *list, size_t index)
222 : {
223 11 : bf_assert(list);
224 :
225 77 : bf_list_foreach (list, node) {
226 38 : if (index == 0)
227 9 : return node->data;
228 29 : --index;
229 : }
230 :
231 : return NULL;
232 : }
|