/src/PROJ/curl/lib/llist.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "curl_memory.h" |
31 | | |
32 | | /* this must be the last include file */ |
33 | | #include "memdebug.h" |
34 | | |
35 | | #ifdef DEBUGBUILD |
36 | | #define LLISTINIT 0x100cc001 /* random pattern */ |
37 | | #define NODEINIT 0x12344321 /* random pattern */ |
38 | | #define NODEREM 0x54321012 /* random pattern */ |
39 | | |
40 | | #define VERIFYNODE(x) verifynode(x) |
41 | | static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) |
42 | | { |
43 | | DEBUGASSERT(!n || (n->_init == NODEINIT)); |
44 | | return n; |
45 | | } |
46 | | #else |
47 | 0 | #define VERIFYNODE(x) x |
48 | | #endif |
49 | | /* |
50 | | * @unittest: 1300 |
51 | | */ |
52 | | void |
53 | | Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) |
54 | 0 | { |
55 | 0 | l->_size = 0; |
56 | 0 | l->_dtor = dtor; |
57 | 0 | l->_head = NULL; |
58 | 0 | l->_tail = NULL; |
59 | | #ifdef DEBUGBUILD |
60 | | l->_init = LLISTINIT; |
61 | | #endif |
62 | 0 | } |
63 | | |
64 | | /* |
65 | | * Curl_llist_insert_next() |
66 | | * |
67 | | * Inserts a new list element after the given one 'e'. If the given existing |
68 | | * entry is NULL and the list already has elements, the new one will be |
69 | | * inserted first in the list. |
70 | | * |
71 | | * The 'ne' argument should be a pointer into the object to store. |
72 | | * |
73 | | * @unittest: 1300 |
74 | | */ |
75 | | void |
76 | | Curl_llist_insert_next(struct Curl_llist *list, |
77 | | struct Curl_llist_node *e, /* may be NULL */ |
78 | | const void *p, |
79 | | struct Curl_llist_node *ne) |
80 | 0 | { |
81 | 0 | DEBUGASSERT(list); |
82 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
83 | 0 | DEBUGASSERT(ne); |
84 | |
|
85 | | #ifdef DEBUGBUILD |
86 | | ne->_init = NODEINIT; |
87 | | #endif |
88 | 0 | ne->_ptr = CURL_UNCONST(p); |
89 | 0 | ne->_list = list; |
90 | 0 | if(list->_size == 0) { |
91 | 0 | list->_head = ne; |
92 | 0 | list->_head->_prev = NULL; |
93 | 0 | list->_head->_next = NULL; |
94 | 0 | list->_tail = ne; |
95 | 0 | } |
96 | 0 | else { |
97 | | /* if 'e' is NULL here, we insert the new element first in the list */ |
98 | 0 | ne->_next = e ? e->_next : list->_head; |
99 | 0 | ne->_prev = e; |
100 | 0 | if(!e) { |
101 | 0 | list->_head->_prev = ne; |
102 | 0 | list->_head = ne; |
103 | 0 | } |
104 | 0 | else if(e->_next) { |
105 | 0 | e->_next->_prev = ne; |
106 | 0 | } |
107 | 0 | else { |
108 | 0 | list->_tail = ne; |
109 | 0 | } |
110 | 0 | if(e) |
111 | 0 | e->_next = ne; |
112 | 0 | } |
113 | |
|
114 | 0 | ++list->_size; |
115 | 0 | } |
116 | | |
117 | | /* |
118 | | * Curl_llist_append() |
119 | | * |
120 | | * Adds a new list element to the end of the list. |
121 | | * |
122 | | * The 'ne' argument should be a pointer into the object to store. |
123 | | * |
124 | | * @unittest: 1300 |
125 | | */ |
126 | | void |
127 | | Curl_llist_append(struct Curl_llist *list, const void *p, |
128 | | struct Curl_llist_node *ne) |
129 | 0 | { |
130 | 0 | DEBUGASSERT(list); |
131 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
132 | 0 | DEBUGASSERT(ne); |
133 | 0 | Curl_llist_insert_next(list, list->_tail, p, ne); |
134 | 0 | } |
135 | | |
136 | | void *Curl_node_take_elem(struct Curl_llist_node *e) |
137 | 0 | { |
138 | 0 | void *ptr; |
139 | 0 | struct Curl_llist *list; |
140 | 0 | if(!e) |
141 | 0 | return NULL; |
142 | | |
143 | 0 | list = e->_list; |
144 | 0 | DEBUGASSERT(list); |
145 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
146 | 0 | DEBUGASSERT(list->_size); |
147 | 0 | DEBUGASSERT(e->_init == NODEINIT); |
148 | 0 | if(list) { |
149 | 0 | if(e == list->_head) { |
150 | 0 | list->_head = e->_next; |
151 | |
|
152 | 0 | if(!list->_head) |
153 | 0 | list->_tail = NULL; |
154 | 0 | else |
155 | 0 | e->_next->_prev = NULL; |
156 | 0 | } |
157 | 0 | else { |
158 | 0 | if(e->_prev) |
159 | 0 | e->_prev->_next = e->_next; |
160 | |
|
161 | 0 | if(!e->_next) |
162 | 0 | list->_tail = e->_prev; |
163 | 0 | else |
164 | 0 | e->_next->_prev = e->_prev; |
165 | 0 | } |
166 | 0 | --list->_size; |
167 | 0 | } |
168 | 0 | ptr = e->_ptr; |
169 | |
|
170 | 0 | e->_list = NULL; |
171 | 0 | e->_ptr = NULL; |
172 | 0 | e->_prev = NULL; |
173 | 0 | e->_next = NULL; |
174 | | #ifdef DEBUGBUILD |
175 | | e->_init = NODEREM; /* specific pattern on remove - not zero */ |
176 | | #endif |
177 | |
|
178 | 0 | return ptr; |
179 | 0 | } |
180 | | |
181 | | /* |
182 | | * @unittest: 1300 |
183 | | */ |
184 | | UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *); |
185 | | UNITTEST void |
186 | | Curl_node_uremove(struct Curl_llist_node *e, void *user) |
187 | 0 | { |
188 | 0 | struct Curl_llist *list; |
189 | 0 | void *ptr; |
190 | 0 | if(!e) |
191 | 0 | return; |
192 | | |
193 | 0 | list = e->_list; |
194 | 0 | DEBUGASSERT(list); |
195 | 0 | if(list) { |
196 | 0 | ptr = Curl_node_take_elem(e); |
197 | 0 | if(list->_dtor) |
198 | 0 | list->_dtor(user, ptr); |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | void Curl_node_remove(struct Curl_llist_node *e) |
203 | 0 | { |
204 | 0 | Curl_node_uremove(e, NULL); |
205 | 0 | } |
206 | | |
207 | | void |
208 | | Curl_llist_destroy(struct Curl_llist *list, void *user) |
209 | 0 | { |
210 | 0 | if(list) { |
211 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
212 | 0 | while(list->_size > 0) |
213 | 0 | Curl_node_uremove(list->_tail, user); |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | /* Curl_llist_head() returns the first 'struct Curl_llist_node *', which |
218 | | might be NULL */ |
219 | | struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) |
220 | 0 | { |
221 | 0 | DEBUGASSERT(list); |
222 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
223 | 0 | return VERIFYNODE(list->_head); |
224 | 0 | } |
225 | | |
226 | | #ifdef UNITTESTS |
227 | | /* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which |
228 | | might be NULL */ |
229 | | struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) |
230 | | { |
231 | | DEBUGASSERT(list); |
232 | | DEBUGASSERT(list->_init == LLISTINIT); |
233 | | return VERIFYNODE(list->_tail); |
234 | | } |
235 | | #endif |
236 | | |
237 | | /* Curl_llist_count() returns a size_t the number of nodes in the list */ |
238 | | size_t Curl_llist_count(struct Curl_llist *list) |
239 | 0 | { |
240 | 0 | DEBUGASSERT(list); |
241 | 0 | DEBUGASSERT(list->_init == LLISTINIT); |
242 | 0 | return list->_size; |
243 | 0 | } |
244 | | |
245 | | /* Curl_node_elem() returns the custom data from a Curl_llist_node */ |
246 | | void *Curl_node_elem(struct Curl_llist_node *n) |
247 | 0 | { |
248 | 0 | DEBUGASSERT(n); |
249 | 0 | DEBUGASSERT(n->_init == NODEINIT); |
250 | 0 | return n->_ptr; |
251 | 0 | } |
252 | | |
253 | | /* Curl_node_next() returns the next element in a list from a given |
254 | | Curl_llist_node */ |
255 | | struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) |
256 | 0 | { |
257 | 0 | DEBUGASSERT(n); |
258 | 0 | DEBUGASSERT(n->_init == NODEINIT); |
259 | 0 | return VERIFYNODE(n->_next); |
260 | 0 | } |
261 | | |
262 | | #ifdef UNITTESTS |
263 | | |
264 | | /* Curl_node_prev() returns the previous element in a list from a given |
265 | | Curl_llist_node */ |
266 | | struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) |
267 | | { |
268 | | DEBUGASSERT(n); |
269 | | DEBUGASSERT(n->_init == NODEINIT); |
270 | | return VERIFYNODE(n->_prev); |
271 | | } |
272 | | |
273 | | #endif |
274 | | |
275 | | struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) |
276 | 0 | { |
277 | 0 | DEBUGASSERT(n); |
278 | 0 | DEBUGASSERT(!n->_list || n->_init == NODEINIT); |
279 | 0 | return n->_list; |
280 | 0 | } |