Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | | * |
10 | | * This software is licensed as described in the file COPYING, which |
11 | | * you should have received as part of this distribution. The terms |
12 | | * are also available at https://curl.se/docs/copyright.html. |
13 | | * |
14 | | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | | * copies of the Software, and permit persons to whom the Software is |
16 | | * furnished to do so, under the terms of the COPYING file. |
17 | | * |
18 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | | * KIND, either express or implied. |
20 | | * |
21 | | * SPDX-License-Identifier: curl |
22 | | * |
23 | | ***************************************************************************/ |
24 | | |
25 | | #include "curl_setup.h" |
26 | | |
27 | | #include <curl/curl.h> |
28 | | |
29 | | #include "llist.h" |
30 | | |
31 | | #ifdef DEBUGBUILD |
32 | 0 | #define LLISTINIT 0x100cc001 /* random pattern */ |
33 | 0 | #define NODEINIT 0x12344321 /* random pattern */ |
34 | 0 | #define NODEREM 0x54321012 /* random pattern */ |
35 | | |
36 | 0 | #define VERIFYNODE(x) verifynode(x) |
37 | | static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) |
38 | 0 | { |
39 | 0 | DEBUGASSERT(!n || (n->_init == NODEINIT)); |
40 | 0 | return n; |
41 | 0 | } |
42 | | #else |
43 | | #define VERIFYNODE(x) x |
44 | | #endif |
45 | | /* |
46 | | * @unittest: 1300 |
47 | | */ |
48 | | void |
49 | | Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) |
50 | 0 | { |
51 | 0 | l->_size = 0; |
52 | 0 | l->_dtor = dtor; |
53 | 0 | l->_head = NULL; |
54 | 0 | l->_tail = NULL; |
55 | 0 | #ifdef DEBUGBUILD |
56 | 0 | l->_init = LLISTINIT; |
57 | 0 | #endif |
58 | 0 | } |
59 | | |
60 | | /* |
61 | | * Curl_llist_insert_next() |
62 | | * |
63 | | * Inserts a new list element after the given one 'e'. If the given existing |
64 | | * entry is NULL and the list already has elements, the new one will be |
65 | | * inserted first in the list. |
66 | | * |
67 | | * The 'ne' argument should be a pointer into the object to store. |
68 | | * |
69 | | * @unittest: 1300 |
70 | | */ |
71 | | void |
72 | | Curl_llist_insert_next(struct Curl_llist *list, |
73 | | struct Curl_llist_node *e, /* may be NULL */ |
74 | | const void *p, |
75 | | struct Curl_llist_node *ne) |
76 | 0 | { |
77 | 0 | DEBUGASSERT(list); |
78 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
79 | 0 | DEBUGASSERT(ne); |
80 | |
|
81 | 0 | #ifdef DEBUGBUILD |
82 | 0 | ne->_init = NODEINIT; |
83 | 0 | #endif |
84 | 0 | ne->_ptr = CURL_UNCONST(p); |
85 | 0 | ne->_list = list; |
86 | 0 | if(list->_size == 0) { |
87 | 0 | list->_head = ne; |
88 | 0 | list->_head->_prev = NULL; |
89 | 0 | list->_head->_next = NULL; |
90 | 0 | list->_tail = ne; |
91 | 0 | } |
92 | 0 | else { |
93 | | /* if 'e' is NULL here, we insert the new element first in the list */ |
94 | 0 | ne->_next = e ? e->_next : list->_head; |
95 | 0 | ne->_prev = e; |
96 | 0 | if(!e) { |
97 | 0 | list->_head->_prev = ne; |
98 | 0 | list->_head = ne; |
99 | 0 | } |
100 | 0 | else if(e->_next) { |
101 | 0 | e->_next->_prev = ne; |
102 | 0 | } |
103 | 0 | else { |
104 | 0 | list->_tail = ne; |
105 | 0 | } |
106 | 0 | if(e) |
107 | 0 | e->_next = ne; |
108 | 0 | } |
109 | |
|
110 | 0 | ++list->_size; |
111 | 0 | } |
112 | | |
113 | | /* |
114 | | * Curl_llist_append() |
115 | | * |
116 | | * Adds a new list element to the end of the list. |
117 | | * |
118 | | * The 'ne' argument should be a pointer into the object to store. |
119 | | * |
120 | | * @unittest: 1300 |
121 | | */ |
122 | | void |
123 | | Curl_llist_append(struct Curl_llist *list, const void *p, |
124 | | struct Curl_llist_node *ne) |
125 | 0 | { |
126 | 0 | DEBUGASSERT(list); |
127 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
128 | 0 | DEBUGASSERT(ne); |
129 | 0 | Curl_llist_insert_next(list, list->_tail, p, ne); |
130 | 0 | } |
131 | | |
132 | | void *Curl_node_take_elem(struct Curl_llist_node *e) |
133 | 0 | { |
134 | 0 | void *ptr; |
135 | 0 | struct Curl_llist *list; |
136 | 0 | if(!e) |
137 | 0 | return NULL; |
138 | | |
139 | 0 | list = e->_list; |
140 | 0 | DEBUGASSERT(list); |
141 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
142 | 0 | DEBUGASSERT(list->_size); |
143 | 0 | DEBUGASSERT(e->_init == NODEINIT); |
144 | 0 | if(list) { |
145 | 0 | if(e == list->_head) { |
146 | 0 | list->_head = e->_next; |
147 | |
|
148 | 0 | if(!list->_head) |
149 | 0 | list->_tail = NULL; |
150 | 0 | else |
151 | 0 | e->_next->_prev = NULL; |
152 | 0 | } |
153 | 0 | else { |
154 | 0 | if(e->_prev) |
155 | 0 | e->_prev->_next = e->_next; |
156 | |
|
157 | 0 | if(!e->_next) |
158 | 0 | list->_tail = e->_prev; |
159 | 0 | else |
160 | 0 | e->_next->_prev = e->_prev; |
161 | 0 | } |
162 | 0 | --list->_size; |
163 | 0 | } |
164 | 0 | ptr = e->_ptr; |
165 | |
|
166 | 0 | e->_list = NULL; |
167 | 0 | e->_ptr = NULL; |
168 | 0 | e->_prev = NULL; |
169 | 0 | e->_next = NULL; |
170 | 0 | #ifdef DEBUGBUILD |
171 | 0 | e->_init = NODEREM; /* specific pattern on remove - not zero */ |
172 | 0 | #endif |
173 | |
|
174 | 0 | return ptr; |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | * @unittest: 1300 |
179 | | */ |
180 | | UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *); |
181 | | UNITTEST void |
182 | | Curl_node_uremove(struct Curl_llist_node *e, void *user) |
183 | 0 | { |
184 | 0 | struct Curl_llist *list; |
185 | 0 | void *ptr; |
186 | 0 | if(!e) |
187 | 0 | return; |
188 | | |
189 | 0 | list = e->_list; |
190 | 0 | DEBUGASSERT(list); |
191 | 0 | if(list) { |
192 | 0 | ptr = Curl_node_take_elem(e); |
193 | 0 | if(list->_dtor) |
194 | 0 | list->_dtor(user, ptr); |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | void Curl_node_remove(struct Curl_llist_node *e) |
199 | 0 | { |
200 | 0 | Curl_node_uremove(e, NULL); |
201 | 0 | } |
202 | | |
203 | | void |
204 | | Curl_llist_destroy(struct Curl_llist *list, void *user) |
205 | 0 | { |
206 | 0 | if(list) { |
207 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
208 | 0 | while(list->_size > 0) |
209 | 0 | Curl_node_uremove(list->_tail, user); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which |
214 | | might be NULL */ |
215 | | struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) |
216 | 0 | { |
217 | 0 | DEBUGASSERT(list); |
218 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
219 | 0 | return VERIFYNODE(list->_head); |
220 | 0 | } |
221 | | |
222 | | #ifdef UNITTESTS |
223 | | /* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which |
224 | | might be NULL */ |
225 | | struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) |
226 | | { |
227 | | DEBUGASSERT(list); |
228 | | DEBUGASSERT(list->_init == LLISTINIT); |
229 | | return VERIFYNODE(list->_tail); |
230 | | } |
231 | | #endif |
232 | | |
233 | | /* Curl_llist_count() returns a size_t the number of nodes in the list */ |
234 | | size_t Curl_llist_count(struct Curl_llist *list) |
235 | 0 | { |
236 | 0 | DEBUGASSERT(list); |
237 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
238 | 0 | return list->_size; |
239 | 0 | } |
240 | | |
241 | | /* Curl_node_elem() returns the custom data from a Curl_llist_node */ |
242 | | void *Curl_node_elem(struct Curl_llist_node *n) |
243 | 0 | { |
244 | 0 | DEBUGASSERT(n); |
245 | 0 | DEBUGASSERT(n->_init == NODEINIT); |
246 | 0 | return n->_ptr; |
247 | 0 | } |
248 | | |
249 | | /* Curl_node_next() returns the next element in a list from a given |
250 | | Curl_llist_node */ |
251 | | struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) |
252 | 0 | { |
253 | 0 | DEBUGASSERT(n); |
254 | 0 | DEBUGASSERT(n->_init == NODEINIT); |
255 | 0 | return VERIFYNODE(n->_next); |
256 | 0 | } |
257 | | |
258 | | #ifdef UNITTESTS |
259 | | |
260 | | /* Curl_node_prev() returns the previous element in a list from a given |
261 | | Curl_llist_node */ |
262 | | struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) |
263 | | { |
264 | | DEBUGASSERT(n); |
265 | | DEBUGASSERT(n->_init == NODEINIT); |
266 | | return VERIFYNODE(n->_prev); |
267 | | } |
268 | | |
269 | | #endif |
270 | | |
271 | | struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) |
272 | 0 | { |
273 | 0 | DEBUGASSERT(n); |
274 | 0 | DEBUGASSERT(!n->_list || n->_init == NODEINIT); |
275 | 0 | return n->_list; |
276 | 0 | } |