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